From 93693bb9b1da33e65ff1aa88eea4031715e8b973 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Sat, 6 Nov 2010 18:42:43 -0700 Subject: Making 'generate_html' return a string rather than dealing with files itself; that stuff is pushed in to 'process'. --- pycco/main.py | 51 +++++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index c853959..a77577d 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -__all__ = ("process",) +__all__ = ("process", "generate_documentation") # **Pycco** is a Python port of [Docco](http://jashkenas.github.com/docco/ ): # the original quick-and-dirty, hundred-line-long, literate-programming-style @@ -31,7 +31,7 @@ def generate_documentation(source, options): fh = open(source, "r") sections = parse(source, fh.read()) highlight(source, sections, options) - generate_html(source, sections, options=options) + return generate_html(source, sections, options=options) # Given a string of source code, parse out each comment and the code that # follows it, and create an individual **section** for it. @@ -194,23 +194,15 @@ def highlight(source, sections, options): def generate_html(source, sections, options): title = path.basename(source) dest = destination(source, options) - html = pycco_template({ - "title": title, - "stylesheet": path.relpath(path.join(options['dir'], "pycco.css"), - path.split(dest)[0]), - "sections": sections, - "source": source, - "path": path, - "destination": destination - }) - print "pycco = %s -> %s" % (source, dest) - try: - os.makedirs(path.split(dest)[0]) - except OSError: - pass - fh = open(dest, "w") - fh.write(html.encode("utf-8")) - fh.close() + return pycco_template({ + "title" : title, + "stylesheet" : path.relpath(path.join(options['dir'], "pycco.css"), + path.split(dest)[0]), + "sections" : sections, + "source" : source, + "path" : path, + "destination" : destination + }).encode("utf-8") #### Helpers & Setup @@ -242,13 +234,13 @@ languages = { ".cpp": { "name": "cpp", "symbol": "//"}, - ".js": { "name": "javascript", "symbol": "//", + ".js": { "name": "javascript", "symbol": "//", "multistart": "/*", "multiend": "*/"}, ".rb": { "name": "ruby", "symbol": "#", "multistart": "=begin", "multiend": "=end"}, - ".py": { "name": "python", "symbol": "#", + ".py": { "name": "python", "symbol": "#", "multistart": '"""', "multiend": '"""' }, ".scm": { "name": "scheme", "symbol": ";;", @@ -320,7 +312,7 @@ def template(source): # Create the template that we will use to generate the Pycco HTML page. pycco_template = template(pycco_resources.html) -# The CSS styles we"d like to apply to the documentation. +# The CSS styles we'd like to apply to the documentation. pycco_styles = pycco_resources.css # The start of each Pygments highlight block. @@ -329,7 +321,6 @@ highlight_start = "
"
 # The end of each Pygments highlight block.
 highlight_end = "
" -# The bulk of the work is done here # For each source file passed in as an argument, generate the documentation. def process(sources, options): sources.sort() @@ -340,7 +331,19 @@ def process(sources, options): css.close() def next_file(): - generate_documentation(sources.pop(0), options) + s = sources.pop(0) + dest = destination(s, options) + + try: + os.makedirs(path.split(dest)[0]) + except OSError: + pass + + with open(destination(s, options), "w") as f: + f.write(generate_documentation(s, options)) + + print "pycco = %s -> %s" % (s, dest) + if sources: next_file() next_file() -- cgit v1.2.1 From dc7190b7eed4a9b38d2a31bba5c0534db816d04a Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Sat, 6 Nov 2010 18:45:49 -0700 Subject: Moving the __all__ to the bottom --- pycco/main.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index a77577d..c06efa9 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -1,7 +1,5 @@ #!/usr/bin/env python -__all__ = ("process", "generate_documentation") - # **Pycco** is a Python port of [Docco](http://jashkenas.github.com/docco/ ): # the original quick-and-dirty, hundred-line-long, literate-programming-style # documentation generator. It produces HTML that displays your comments @@ -348,6 +346,7 @@ def process(sources, options): next_file() next_file() +__all__ = ("process", "generate_documentation") # Hook spot for the console script def main(): -- cgit v1.2.1 From f31aaf2d98eccccfe98344aa14dca065b070905a Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Sat, 6 Nov 2010 18:46:10 -0700 Subject: Adding a period to the end of the sentence --- pycco/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pycco/main.py b/pycco/main.py index c06efa9..c9473a3 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -348,7 +348,7 @@ def process(sources, options): __all__ = ("process", "generate_documentation") -# Hook spot for the console script +# Hook spot for the console script. def main(): parser = optparse.OptionParser() parser.add_option('-p', '--paths', action='store_true', -- cgit v1.2.1 From 2689409919d7fc116bd42a89df35b61f4b09606d Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Sat, 6 Nov 2010 18:54:08 -0700 Subject: Updating some of the comments --- pycco/main.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index c9473a3..e85278f 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -12,18 +12,23 @@ # # pycco src/*.py # -# ...will generate linked HTML documentation for the named source files, saving -# it into a `docs` folder by default. +# This will generate linked HTML documentation for the named source files, +# saving it into a `docs` folder by default. # # To install Pycco, simply # -# sudo setup.py install +# pip install pycco # +# Or, to install the latest source +# +# git clone git://github.com/fitzgen/pycco.git +# cd pycco +# python setup.py install #### Main Documentation Generation Functions -# Generate the documentation for a source file by reading it in, splitting it -# up into comment/code sections, highlighting them for the appropriate language, +# Generate the documentation for a source file by reading it in, splitting it up +# into comment/code sections, highlighting them for the appropriate language, # and merging them into an HTML template. def generate_documentation(source, options): fh = open(source, "r") -- cgit v1.2.1 From 5f96e16fbcc12a4189e2e369def5d71c12a0bc51 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Sat, 6 Nov 2010 18:54:41 -0700 Subject: Regenerating the AUTHORS file --- AUTHORS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AUTHORS b/AUTHORS index 9c50a4a..c606ffa 100644 --- a/AUTHORS +++ b/AUTHORS @@ -2,5 +2,7 @@ Alexis Metaireau Anders Bergh Antti Kaihola Jack Miller +Morgan Goose Nick Fitzgerald +goosemo khamidou -- cgit v1.2.1 From ba01f0f21fd0a42e37fecb40d81b4dcb6b816911 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Sat, 6 Nov 2010 20:05:50 -0700 Subject: Replacing options with keyword args, and updating the preprocessor a little --- pycco/main.py | 89 ++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 58 insertions(+), 31 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index e85278f..a2edccc 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -30,11 +30,13 @@ # Generate the documentation for a source file by reading it in, splitting it up # into comment/code sections, highlighting them for the appropriate language, # and merging them into an HTML template. -def generate_documentation(source, options): +def generate_documentation(source, outdir=None, preserve_paths=True): + if not outdir: + raise TypeError("Missing the required 'outdir' keyword argument.") fh = open(source, "r") sections = parse(source, fh.read()) - highlight(source, sections, options) - return generate_html(source, sections, options=options) + highlight(source, sections, preserve_paths=preserve_paths, outdir=outdir) + return generate_html(source, sections, preserve_paths=preserve_paths, outdir=outdir) # Given a string of source code, parse out each comment and the code that # follows it, and create an individual **section** for it. @@ -139,28 +141,43 @@ def parse(source, code): # === Preprocessing the comments === # -# Add cross-references before having the text processed by markdown. -# It's possible to reference another file, like this : [[pycco.py]] or a specific section of -# another file, like this: [[pycco.py#Highlighting]]. Of course, sections have to be manually declared before, -# A section name is written on a single line, and surrounded by equals signs, === like this === -def preprocess(comment, section_nr, options): +# Add cross-references before having the text processed by markdown. It's +# possible to reference another file, like this : `[[main.py]]` which renders +# [[main.py]]. You can also reference a specific section of another file, like +# this: `[[main.py#highlighting-the-source-code]]` which renders as +# [[main.py#highlighting-the-source-code]]. Sections have to be manually +# declared; they are written on a single line, and surrounded by equals signs: +# `=== like this ===` +def preprocess(comment, section_nr, preserve_paths=True, outdir=None): + if not outdir: + raise TypeError("Missing the required 'outdir' keyword argument.") def sanitize_section_name(name): - return name.strip().split(" ")[0] + return "-".join(name.lower().strip().split(" ")) def replace_crossref(match): # Check if the match contains an anchor if '#' in match.group(1): name, anchor = match.group(1).split('#') - return "[%s](%s#%s)" % (name, path.basename(destination(name, options)), anchor) + return " [%s](%s#%s)" % (name, + path.basename(destination(name, + preserve_paths=preserve_paths, + outdir=outdir)), + anchor) else: - return "[%s](%s)" % (match.group(1), path.basename(destination(match.group(1), options))) + return " [%s](%s)" % (match.group(1), + path.basename(destination(match.group(1), + preserve_paths=preserve_paths, + outdir=outdir))) def replace_section_name(match): - return '*%s*' % (sanitize_section_name(match.group(1)), match.group(1)) + return '### %(name)s' % { + "id" : sanitize_section_name(match.group(1)), + "name" : match.group(1) + } - comment = re.sub('===(.+)===\\n', replace_section_name, comment) - comment = re.sub('\[\[(.+)\]\]', replace_crossref, comment) + comment = re.sub('^===(.+)===\\n', replace_section_name, comment) + comment = re.sub('[^`]\[\[(.+)\]\]', replace_crossref, comment) return comment @@ -172,7 +189,9 @@ def preprocess(comment, section_nr, options): # We process the entire file in a single call to Pygments by inserting little # marker comments between each section and then splitting the result string # wherever our markers occur. -def highlight(source, sections, options): +def highlight(source, sections, preserve_paths=True, outdir=None): + if not outdir: + raise TypeError("Missing the required 'outdir' keyword argument.") language = get_language(source) output = pygments.highlight(language["divider_text"].join(section["code_text"] for section in sections), @@ -187,19 +206,24 @@ def highlight(source, sections, options): docs_text = unicode(section["docs_text"]) except UnicodeError: docs_text = unicode(section["docs_text"].decode('utf-8')) - section["docs_html"] = markdown(preprocess(docs_text, i, options)) + section["docs_html"] = markdown(preprocess(docs_text, + i, + preserve_paths=preserve_paths, + outdir=outdir)) section["num"] = i # === HTML Code generation === # Once all of the code is finished highlighting, we can generate the HTML file # and write out the documentation. Pass the completed sections into the template # found in `resources/pycco.html` -def generate_html(source, sections, options): +def generate_html(source, sections, preserve_paths=True, outdir=None): + if not outdir: + raise TypeError("Missing the required 'outdir' keyword argument") title = path.basename(source) - dest = destination(source, options) + dest = destination(source, preserve_paths=preserve_paths, outdir=outdir) return pycco_template({ "title" : title, - "stylesheet" : path.relpath(path.join(options['dir'], "pycco.css"), + "stylesheet" : path.relpath(path.join(path.dirname(dest), "pycco.css"), path.split(dest)[0]), "sections" : sections, "source" : source, @@ -286,15 +310,16 @@ def get_language(source): # Compute the destination HTML path for an input source file path. If the source # is `lib/example.py`, the HTML will be at `docs/example.html` -def destination(filepath, options): - preserve_paths = options['paths'] +def destination(filepath, preserve_paths=True, outdir=None): + if not outdir: + raise TypeError("Missing the required 'outdir' keyword argument.") try: name = filepath.replace(filepath[ filepath.rindex("."): ], "") except ValueError: name = filepath if not preserve_paths: name = path.basename(name) - return path.join(options['dir'], "%s.html" % name) + return path.join(outdir, "%s.html" % name) # Shift items off the front of the `list` until it is empty, then return # `default`. @@ -325,25 +350,28 @@ highlight_start = "
"
 highlight_end = "
" # For each source file passed in as an argument, generate the documentation. -def process(sources, options): +def process(sources, preserve_paths=True, outdir=None): + if not outdir: + raise TypeError("Missing the required 'outdir' keyword argument.") + sources.sort() if sources: - ensure_directory(options['dir']) - css = open(path.join(options['dir'], "pycco.css"), "w") + ensure_directory(outdir) + css = open(path.join(outdir, "pycco.css"), "w") css.write(pycco_styles) css.close() def next_file(): s = sources.pop(0) - dest = destination(s, options) + dest = destination(s, preserve_paths=preserve_paths, outdir=outdir) try: os.makedirs(path.split(dest)[0]) except OSError: pass - with open(destination(s, options), "w") as f: - f.write(generate_documentation(s, options)) + with open(destination(s, preserve_paths=preserve_paths, outdir=outdir), "w") as f: + f.write(generate_documentation(s, outdir=outdir)) print "pycco = %s -> %s" % (s, dest) @@ -360,13 +388,12 @@ def main(): help='Preserve path structure of original files') parser.add_option('-d', '--directory', action='store', type='string', - dest='dir', default='docs', + dest='outdir', default='docs', help='The output directory that the rendered files should go to.') opts, sources = parser.parse_args() - process(sources, opts.__dict__) + process(sources, outdir=opts.outdir, preserve_paths=opts.paths) # Run the script. if __name__ == "__main__": main() - -- cgit v1.2.1 From 4020dad276f7d6d91afe250d224651ed8b8ac4bc Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Sat, 6 Nov 2010 20:07:41 -0700 Subject: Fixing the comments sections up a litle --- pycco/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pycco/main.py b/pycco/main.py index a2edccc..0235f9b 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -25,7 +25,7 @@ # cd pycco # python setup.py install -#### Main Documentation Generation Functions +# === Main Documentation Generation Functions === # Generate the documentation for a source file by reading it in, splitting it up # into comment/code sections, highlighting them for the appropriate language, @@ -213,6 +213,7 @@ def highlight(source, sections, preserve_paths=True, outdir=None): section["num"] = i # === HTML Code generation === +# # Once all of the code is finished highlighting, we can generate the HTML file # and write out the documentation. Pass the completed sections into the template # found in `resources/pycco.html` -- cgit v1.2.1 From eb177f8aae61197d494d2356dada78bab72f76c4 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Sat, 6 Nov 2010 20:19:35 -0700 Subject: little things to readme and sections --- README | 6 +++--- pycco/main.py | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README b/README index 0ee0935..5343728 100644 --- a/README +++ b/README @@ -10,9 +10,9 @@ Y8b d88P "Y88P" -Pycco is a Python port of Docco: the original quick-and-dirty, -hundred-line-long, literate-programming-style documentation generator. For more -information, see: +Pycco is a Python port of Docco: the original quick-and-dirty, hundred-line- +long, literate-programming-style documentation generator. For more information, +see: http://fitzgen.github.com/pycco/ diff --git a/pycco/main.py b/pycco/main.py index 0235f9b..19abe0d 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -140,7 +140,7 @@ def parse(source, code): return sections # === Preprocessing the comments === -# + # Add cross-references before having the text processed by markdown. It's # possible to reference another file, like this : `[[main.py]]` which renders # [[main.py]]. You can also reference a specific section of another file, like @@ -182,7 +182,7 @@ def preprocess(comment, section_nr, preserve_paths=True, outdir=None): return comment # === Highlighting the source code === -# + # Highlights a single chunk of code using the **Pygments** module, and runs the # text of its corresponding comment through **Markdown**. # @@ -213,7 +213,7 @@ def highlight(source, sections, preserve_paths=True, outdir=None): section["num"] = i # === HTML Code generation === -# + # Once all of the code is finished highlighting, we can generate the HTML file # and write out the documentation. Pass the completed sections into the template # found in `resources/pycco.html` @@ -232,7 +232,7 @@ def generate_html(source, sections, preserve_paths=True, outdir=None): "destination" : destination }).encode("utf-8") -#### Helpers & Setup +# === Helpers & Setup === # This module contains all of our static resources. import pycco_resources -- cgit v1.2.1 From 85a7bcafa6fa1e9ff90baca8c0d8701e6ed023ba Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Sat, 20 Nov 2010 04:34:13 -0800 Subject: Adding support for erlang --- pycco/main.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pycco/main.py b/pycco/main.py index 19abe0d..0c671b8 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -276,6 +276,8 @@ languages = { ".lua": { "name": "lua", "symbol": "--", "multistart": "--[[", "mutliend": "--]]"}, + + ".erl": { "name": "erlang", "symbol": "%%" }, } # Build out the appropriate matchers and delimiters for each language. -- cgit v1.2.1 From 6cec10f84697447d75bfa2cc3b8c7db355808ac5 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Sat, 20 Nov 2010 18:30:55 -0800 Subject: Fixing bug where languages w/out multi-line delimiters were failing --- pycco/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index 0c671b8..16eca7c 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -74,13 +74,13 @@ def parse(source, code): # Setup the variables to get ready to check for multiline comments preformatted = multi_line = False last_scope = 0 - multi_line_delimeters = [language["multistart"], language["multiend"]] + multi_line_delimeters = [language.get("multistart"), language.get("multiend")] for line in lines: # Only go into multiline comments section when one of the delimeters is # found to be at the start of a line - if any([line.lstrip().startswith(delim) for delim in multi_line_delimeters]): + if all(multi_line_delimeters) and any([line.lstrip().startswith(delim) for delim in multi_line_delimeters]): if not multi_line: multi_line = True -- cgit v1.2.1 From 02ce821673c906b64999600914ccb92187addb94 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Sat, 20 Nov 2010 18:52:54 -0800 Subject: Tweaking some styles --- pycco_resources/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pycco_resources/__init__.py b/pycco_resources/__init__.py index 4f6af45..98ea841 100644 --- a/pycco_resources/__init__.py +++ b/pycco_resources/__init__.py @@ -19,8 +19,8 @@ p { h1, h2, h3, h4, h5, h6 { margin: 40px 0 15px 0; } - h3, h4, h5, h6 { - margin-top: 20px; +h2, h3, h4, h5, h6 { + margin-top: 0; } #container { position: relative; @@ -114,7 +114,7 @@ table td { border-left: 1px solid #e5e5ee; } pre, tt, code { - font-size: 12px; line-height: 18px; + line-height: 18px; font-family: Monaco, Consolas, "Lucida Console", monospace; margin: 0; padding: 0; } -- cgit v1.2.1 From 0df2a65cd297a696e53af449b28e3fff48428fc1 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Mon, 22 Nov 2010 00:19:32 -0800 Subject: Fixing bug with interaction with pystache and recursive rendering of context variables --- pycco/main.py | 17 ++++++++++++++--- pycco_resources/__init__.py | 3 +++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index 16eca7c..f9e5fa4 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -216,13 +216,22 @@ def highlight(source, sections, preserve_paths=True, outdir=None): # Once all of the code is finished highlighting, we can generate the HTML file # and write out the documentation. Pass the completed sections into the template -# found in `resources/pycco.html` +# found in `resources/pycco.html`. +# +# Pystache will attempt to recursively render context variables, so we must +# replace any occurences of `{{`, which is valid in some languages, with a +# "unique enough" identifier before rendering, and then post-process the +# rendered template and change the identifier back to `{{`. def generate_html(source, sections, preserve_paths=True, outdir=None): if not outdir: raise TypeError("Missing the required 'outdir' keyword argument") title = path.basename(source) dest = destination(source, preserve_paths=preserve_paths, outdir=outdir) - return pycco_template({ + + for sect in sections: + sect["code_html"] = re.sub(r"\{\{", r"__DOUBLE_OPEN_STACHE__", sect["code_html"]) + + rendered = pycco_template({ "title" : title, "stylesheet" : path.relpath(path.join(path.dirname(dest), "pycco.css"), path.split(dest)[0]), @@ -230,7 +239,9 @@ def generate_html(source, sections, preserve_paths=True, outdir=None): "source" : source, "path" : path, "destination" : destination - }).encode("utf-8") + }) + + return re.sub(r"__DOUBLE_OPEN_STACHE__", "{{", rendered).encode("utf-8") # === Helpers & Setup === diff --git a/pycco_resources/__init__.py b/pycco_resources/__init__.py index 98ea841..69700d7 100644 --- a/pycco_resources/__init__.py +++ b/pycco_resources/__init__.py @@ -113,6 +113,9 @@ table td { background: #f5f5ff; border-left: 1px solid #e5e5ee; } +.code pre, .docs p code { + font-size: 12px; +} pre, tt, code { line-height: 18px; font-family: Monaco, Consolas, "Lucida Console", monospace; -- cgit v1.2.1 From b83367527a2e6502cb67e40538fa9d77d88ddb34 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Mon, 22 Nov 2010 00:23:08 -0800 Subject: Adding to gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index f749ab2..07b20dd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ *.pyc /Pycco.egg-info +build/* +dist/* +docs/* \ No newline at end of file -- cgit v1.2.1 From 9376e354d6b53042a51dd9af15de5bd3f7d91f36 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Mon, 22 Nov 2010 00:23:25 -0800 Subject: Bumping version number --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index cffb9e8..6686b19 100644 --- a/setup.py +++ b/setup.py @@ -2,8 +2,8 @@ from setuptools import setup, find_packages setup( name = "Pycco", - version = "0.1.2", - description = """A Python port of Docco: the original quick-and-dirty, + version = "0.2.0", + description = """A Python port of Docco: the original quick-and-dirty, hundred-line-long, literate-programming-style documentation generator. """, author = "Nick Fitzgerald", -- cgit v1.2.1 From b58bee05ebaf36b24edf45689094b63265267551 Mon Sep 17 00:00:00 2001 From: Christopher Gateley Date: Sat, 4 Dec 2010 07:10:28 -0600 Subject: Added support for multiple header levels. Previously only h3 was allowed. --- pycco/main.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index f9e5fa4..18bf085 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -171,12 +171,13 @@ def preprocess(comment, section_nr, preserve_paths=True, outdir=None): outdir=outdir))) def replace_section_name(match): - return '### %(name)s' % { - "id" : sanitize_section_name(match.group(1)), - "name" : match.group(1) + return '%(lvl)s %(name)s' % { + "lvl" : re.sub('=', '#', match.group(1)), + "id" : sanitize_section_name(match.group(2)), + "name" : match.group(2) } - comment = re.sub('^===(.+)===\\n', replace_section_name, comment) + comment = re.sub('^([=]+)([^=]+)[=]*\\n', replace_section_name, comment) comment = re.sub('[^`]\[\[(.+)\]\]', replace_crossref, comment) return comment -- cgit v1.2.1 From f7dd87e45dac08a495563b38db67eabc689f00aa Mon Sep 17 00:00:00 2001 From: Christopher Gateley Date: Fri, 10 Dec 2010 22:45:04 -0600 Subject: moved from table to pure css layout design --- pycco_resources/__init__.py | 164 ++++++++++++++------------------------------ 1 file changed, 53 insertions(+), 111 deletions(-) diff --git a/pycco_resources/__init__.py b/pycco_resources/__init__.py index 69700d7..f9535f5 100644 --- a/pycco_resources/__init__.py +++ b/pycco_resources/__init__.py @@ -22,7 +22,7 @@ h1, h2, h3, h4, h5, h6 { h2, h3, h4, h5, h6 { margin-top: 0; } -#container { +#container, div.section { position: relative; } #background { @@ -32,95 +32,57 @@ h2, h3, h4, h5, h6 { border-left: 1px solid #e5e5ee; z-index: -1; } -#jump_to, #jump_page { - background: white; - -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777; - -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px; - font: 10px Arial; - text-transform: uppercase; - cursor: pointer; - text-align: right; +div.docs { + float: left; + max-width: 500px; + min-width: 500px; + min-height: 5px; + padding: 10px 25px 1px 50px; + vertical-align: top; + text-align: left; } -#jump_to, #jump_wrapper { - position: fixed; - right: 0; top: 0; - padding: 5px 10px; -} - #jump_wrapper { - padding: 0; - display: none; + .docs pre { + margin: 15px 0 15px; + padding-left: 15px; } - #jump_to:hover #jump_wrapper { - display: block; - } - #jump_page { - padding: 5px 0 3px; - margin: 0 0 25px 25px; - } - #jump_page .source { - display: block; - padding: 5px 10px; - text-decoration: none; - border-top: 1px solid #eee; - } - #jump_page .source:hover { - background: #f5f5ff; - } - #jump_page .source:first-child { - } -table td { - border: 0; - outline: 0; -} - td.docs, th.docs { - max-width: 500px; - min-width: 500px; - min-height: 5px; - padding: 10px 25px 1px 50px; - vertical-align: top; - text-align: left; + .docs p tt, .docs p code { + background: #f8f8ff; + border: 1px solid #dedede; + font-size: 12px; + padding: 0 0.2em; } - .docs pre { - margin: 15px 0 15px; - padding-left: 15px; - } - .docs p tt, .docs p code { - background: #f8f8ff; - border: 1px solid #dedede; - font-size: 12px; - padding: 0 0.2em; - } - .octowrap { - position: relative; + .octowrap { + position: relative; + } + .octothorpe { + font: 12px Arial; + text-decoration: none; + color: #454545; + position: absolute; + top: 3px; left: -20px; + padding: 1px 2px; + opacity: 0; + -webkit-transition: opacity 0.2s linear; } - .octothorpe { - font: 12px Arial; - text-decoration: none; - color: #454545; - position: absolute; - top: 3px; left: -20px; - padding: 1px 2px; - opacity: 0; - -webkit-transition: opacity 0.2s linear; + div.docs:hover .octothorpe { + opacity: 1; } - td.docs:hover .octothorpe { - opacity: 1; - } - td.code, th.code { - padding: 14px 15px 16px 50px; - width: 100%; - vertical-align: top; - background: #f5f5ff; - border-left: 1px solid #e5e5ee; - } -.code pre, .docs p code { - font-size: 12px; +div.code { + margin-left: 580px; + padding: 14px 15px 16px 50px; + vertical-align: top; } + .code pre, .docs p code { + font-size: 12px; + } pre, tt, code { line-height: 18px; font-family: Monaco, Consolas, "Lucida Console", monospace; margin: 0; padding: 0; } +div.clearall { + clear: both; +} /*---------------------- Syntax Highlighting -----------------------------*/ @@ -198,42 +160,22 @@ html = """\ +
-
- {{#sources?}} -
- Jump To … -
-
- {{#sources}} - {{ basename }} - {{/sources}} + {{#sections}} +
+
+
+ #
+ {{{ docs_html }}} +
+
+ {{{ code_html }}}
- {{/sources?}} - - - - - - - - - {{#sections}} - - - - - {{/sections}} -

{{ title }}

-
- # -
- {{{ docs_html }}} -
-
{{{ code_html }}}
-
+
+ {{/sections}}
""" -- cgit v1.2.1 From f0108e8ae888356e9bb7d7b01947a0022840a9e7 Mon Sep 17 00:00:00 2001 From: Christopher Gateley Date: Fri, 10 Dec 2010 23:11:15 -0600 Subject: added back in unused 'jump-list' specific css and html --- pycco_resources/__init__.py | 51 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/pycco_resources/__init__.py b/pycco_resources/__init__.py index f9535f5..0f320c5 100644 --- a/pycco_resources/__init__.py +++ b/pycco_resources/__init__.py @@ -32,6 +32,42 @@ h2, h3, h4, h5, h6 { border-left: 1px solid #e5e5ee; z-index: -1; } +#jump_to, #jump_page { + background: white; + -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777; + -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px; + font: 10px Arial; + text-transform: uppercase; + cursor: pointer; + text-align: right; +} +#jump_to, #jump_wrapper { + position: fixed; + right: 0; top: 0; + padding: 5px 10px; +} + #jump_wrapper { + padding: 0; + display: none; + } + #jump_to:hover #jump_wrapper { + display: block; + } + #jump_page { + padding: 5px 0 3px; + margin: 0 0 25px 25px; + } + #jump_page .source { + display: block; + padding: 5px 10px; + text-decoration: none; + border-top: 1px solid #eee; + } + #jump_page .source:hover { + background: #f5f5ff; + } + #jump_page .source:first-child { + } div.docs { float: left; max-width: 500px; @@ -162,6 +198,21 @@ html = """\
+ {{#sources?}} +
+ Jump To … +
+
+ {{#sources}} + {{ basename }} + {{/sources}} +
+
+
+ {{/sources?}} +
+

{{ title }}

+
{{#sections}}
-- cgit v1.2.1 From f909871ca1e069f202323a6c68876b96e2fa994e Mon Sep 17 00:00:00 2001 From: Christopher Gateley Date: Fri, 10 Dec 2010 23:31:43 -0600 Subject: explicity disallow sections that have both empty 'doc' and empty 'code' sections --- pycco/main.py | 9 +++++---- pycco_resources/__init__.py | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index 18bf085..74e31dc 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -66,10 +66,11 @@ def parse(source, code): def save(docs, code): - sections.append({ - "docs_text": docs, - "code_text": code - }) + if docs and code: + sections.append({ + "docs_text": docs, + "code_text": code + }) # Setup the variables to get ready to check for multiline comments preformatted = multi_line = False diff --git a/pycco_resources/__init__.py b/pycco_resources/__init__.py index 0f320c5..71456b1 100644 --- a/pycco_resources/__init__.py +++ b/pycco_resources/__init__.py @@ -213,8 +213,9 @@ html = """\

{{ title }}

+
{{#sections}} -
+
# -- cgit v1.2.1 From 11cfb0fd86f8f14fdc17688ac0dd815008022128 Mon Sep 17 00:00:00 2001 From: Christopher Gateley Date: Sat, 11 Dec 2010 00:07:49 -0600 Subject: added smartypants html entity conversion to go alongside markdown conversion. --- pycco/main.py | 9 +++++---- setup.py | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index 18bf085..e44fb7c 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -1,12 +1,13 @@ #!/usr/bin/env python -# **Pycco** is a Python port of [Docco](http://jashkenas.github.com/docco/ ): +# "**Pycco**" is a Python port of [Docco](http://jashkenas.github.com/docco/ ): # the original quick-and-dirty, hundred-line-long, literate-programming-style # documentation generator. It produces HTML that displays your comments # alongside your code. Comments are passed through -# [Markdown](http://daringfireball.net/projects/markdown/syntax), and code is -# passed through [Pygments](http://pygments.org/) syntax highlighting. This -# page is the result of running Pycco against its own source file. +# [Markdown](http://daringfireball.net/projects/markdown/syntax) and +# [SmartyPants](http://daringfireball.net/projects/smartypants), while code is +# passed through [Pygments](http://pygments.org/) for syntax highlighting. +# This page is the result of running Pycco against its own source file. # # If you install Pycco, you can run it from the command-line: # diff --git a/setup.py b/setup.py index 6686b19..c3bc074 100644 --- a/setup.py +++ b/setup.py @@ -15,5 +15,5 @@ setup( 'pycco = pycco.main:main', ] }, - install_requires = ['markdown', 'pygments', 'pystache'], + install_requires = ['markdown', 'pygments', 'pystache', 'smartypants'], ) -- cgit v1.2.1 From e2032e19b05c052b8fad276a1fb6aac1a5b552a8 Mon Sep 17 00:00:00 2001 From: Christopher Gateley Date: Fri, 10 Dec 2010 22:45:04 -0600 Subject: moved from table to pure css layout design --- pycco_resources/__init__.py | 164 ++++++++++++++------------------------------ 1 file changed, 53 insertions(+), 111 deletions(-) diff --git a/pycco_resources/__init__.py b/pycco_resources/__init__.py index 69700d7..f9535f5 100644 --- a/pycco_resources/__init__.py +++ b/pycco_resources/__init__.py @@ -22,7 +22,7 @@ h1, h2, h3, h4, h5, h6 { h2, h3, h4, h5, h6 { margin-top: 0; } -#container { +#container, div.section { position: relative; } #background { @@ -32,95 +32,57 @@ h2, h3, h4, h5, h6 { border-left: 1px solid #e5e5ee; z-index: -1; } -#jump_to, #jump_page { - background: white; - -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777; - -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px; - font: 10px Arial; - text-transform: uppercase; - cursor: pointer; - text-align: right; +div.docs { + float: left; + max-width: 500px; + min-width: 500px; + min-height: 5px; + padding: 10px 25px 1px 50px; + vertical-align: top; + text-align: left; } -#jump_to, #jump_wrapper { - position: fixed; - right: 0; top: 0; - padding: 5px 10px; -} - #jump_wrapper { - padding: 0; - display: none; + .docs pre { + margin: 15px 0 15px; + padding-left: 15px; } - #jump_to:hover #jump_wrapper { - display: block; - } - #jump_page { - padding: 5px 0 3px; - margin: 0 0 25px 25px; - } - #jump_page .source { - display: block; - padding: 5px 10px; - text-decoration: none; - border-top: 1px solid #eee; - } - #jump_page .source:hover { - background: #f5f5ff; - } - #jump_page .source:first-child { - } -table td { - border: 0; - outline: 0; -} - td.docs, th.docs { - max-width: 500px; - min-width: 500px; - min-height: 5px; - padding: 10px 25px 1px 50px; - vertical-align: top; - text-align: left; + .docs p tt, .docs p code { + background: #f8f8ff; + border: 1px solid #dedede; + font-size: 12px; + padding: 0 0.2em; } - .docs pre { - margin: 15px 0 15px; - padding-left: 15px; - } - .docs p tt, .docs p code { - background: #f8f8ff; - border: 1px solid #dedede; - font-size: 12px; - padding: 0 0.2em; - } - .octowrap { - position: relative; + .octowrap { + position: relative; + } + .octothorpe { + font: 12px Arial; + text-decoration: none; + color: #454545; + position: absolute; + top: 3px; left: -20px; + padding: 1px 2px; + opacity: 0; + -webkit-transition: opacity 0.2s linear; } - .octothorpe { - font: 12px Arial; - text-decoration: none; - color: #454545; - position: absolute; - top: 3px; left: -20px; - padding: 1px 2px; - opacity: 0; - -webkit-transition: opacity 0.2s linear; + div.docs:hover .octothorpe { + opacity: 1; } - td.docs:hover .octothorpe { - opacity: 1; - } - td.code, th.code { - padding: 14px 15px 16px 50px; - width: 100%; - vertical-align: top; - background: #f5f5ff; - border-left: 1px solid #e5e5ee; - } -.code pre, .docs p code { - font-size: 12px; +div.code { + margin-left: 580px; + padding: 14px 15px 16px 50px; + vertical-align: top; } + .code pre, .docs p code { + font-size: 12px; + } pre, tt, code { line-height: 18px; font-family: Monaco, Consolas, "Lucida Console", monospace; margin: 0; padding: 0; } +div.clearall { + clear: both; +} /*---------------------- Syntax Highlighting -----------------------------*/ @@ -198,42 +160,22 @@ html = """\ +
-
- {{#sources?}} -
- Jump To … -
-
- {{#sources}} - {{ basename }} - {{/sources}} + {{#sections}} +
+
+
+ #
+ {{{ docs_html }}} +
+
+ {{{ code_html }}}
- {{/sources?}} - - - - - - - - - {{#sections}} - - - - - {{/sections}} -

{{ title }}

-
- # -
- {{{ docs_html }}} -
-
{{{ code_html }}}
-
+
+ {{/sections}}
""" -- cgit v1.2.1 From d73743aa1d4b77f75f4eab207a87e84d77a6df01 Mon Sep 17 00:00:00 2001 From: Christopher Gateley Date: Fri, 10 Dec 2010 23:11:15 -0600 Subject: added back in unused 'jump-list' specific css and html --- pycco_resources/__init__.py | 51 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/pycco_resources/__init__.py b/pycco_resources/__init__.py index f9535f5..0f320c5 100644 --- a/pycco_resources/__init__.py +++ b/pycco_resources/__init__.py @@ -32,6 +32,42 @@ h2, h3, h4, h5, h6 { border-left: 1px solid #e5e5ee; z-index: -1; } +#jump_to, #jump_page { + background: white; + -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777; + -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px; + font: 10px Arial; + text-transform: uppercase; + cursor: pointer; + text-align: right; +} +#jump_to, #jump_wrapper { + position: fixed; + right: 0; top: 0; + padding: 5px 10px; +} + #jump_wrapper { + padding: 0; + display: none; + } + #jump_to:hover #jump_wrapper { + display: block; + } + #jump_page { + padding: 5px 0 3px; + margin: 0 0 25px 25px; + } + #jump_page .source { + display: block; + padding: 5px 10px; + text-decoration: none; + border-top: 1px solid #eee; + } + #jump_page .source:hover { + background: #f5f5ff; + } + #jump_page .source:first-child { + } div.docs { float: left; max-width: 500px; @@ -162,6 +198,21 @@ html = """\
+ {{#sources?}} +
+ Jump To … +
+
+ {{#sources}} + {{ basename }} + {{/sources}} +
+
+
+ {{/sources?}} +
+

{{ title }}

+
{{#sections}}
-- cgit v1.2.1 From bcb8f2c5f5a5adaee51eed9631e56ad7ebf557b5 Mon Sep 17 00:00:00 2001 From: Christopher Gateley Date: Fri, 10 Dec 2010 23:31:43 -0600 Subject: explicity disallow sections that have both empty 'doc' and empty 'code' sections --- pycco/main.py | 9 +++++---- pycco_resources/__init__.py | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index 18bf085..74e31dc 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -66,10 +66,11 @@ def parse(source, code): def save(docs, code): - sections.append({ - "docs_text": docs, - "code_text": code - }) + if docs and code: + sections.append({ + "docs_text": docs, + "code_text": code + }) # Setup the variables to get ready to check for multiline comments preformatted = multi_line = False diff --git a/pycco_resources/__init__.py b/pycco_resources/__init__.py index 0f320c5..71456b1 100644 --- a/pycco_resources/__init__.py +++ b/pycco_resources/__init__.py @@ -213,8 +213,9 @@ html = """\

{{ title }}

+
{{#sections}} -
+
# -- cgit v1.2.1 From f5ab0ee3d2d03b22cb3dfc40705a6c1508f44252 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Sun, 12 Dec 2010 20:06:44 -0800 Subject: Updating AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index c606ffa..442c482 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,6 +1,7 @@ Alexis Metaireau Anders Bergh Antti Kaihola +Christopher Gateley Jack Miller Morgan Goose Nick Fitzgerald -- cgit v1.2.1 From 83805b64aed5bf2c366e074184e21fc289e07870 Mon Sep 17 00:00:00 2001 From: Jesse Dubay Date: Mon, 13 Dec 2010 01:45:57 -0800 Subject: Pedantry! (delimeter -> delimiter) --- pycco/main.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index 80bc9d1..4daf745 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -76,20 +76,20 @@ def parse(source, code): # Setup the variables to get ready to check for multiline comments preformatted = multi_line = False last_scope = 0 - multi_line_delimeters = [language.get("multistart"), language.get("multiend")] + multi_line_delimiters = [language.get("multistart"), language.get("multiend")] for line in lines: - # Only go into multiline comments section when one of the delimeters is + # Only go into multiline comments section when one of the delimiters is # found to be at the start of a line - if all(multi_line_delimeters) and any([line.lstrip().startswith(delim) for delim in multi_line_delimeters]): + if all(multi_line_delimiters) and any([line.lstrip().startswith(delim) for delim in multi_line_delimiters]): if not multi_line: multi_line = True else: multi_line = False - # Get rid of the delimeters so that they aren't in the final docs + # Get rid of the delimiters so that they aren't in the final docs line = re.sub(language["multistart"],'',line) line = re.sub(language["multiend"],'',line) docs_text += line.strip() + '\n' -- cgit v1.2.1 From 6d87e5786991db97f3bdfddd61f72c189f6c314d Mon Sep 17 00:00:00 2001 From: Aaron Blohowiak Date: Mon, 13 Dec 2010 00:22:29 -0800 Subject: make JS work by escaping the start and end symbols --- pycco/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index 4daf745..67efe41 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -90,8 +90,8 @@ def parse(source, code): multi_line = False # Get rid of the delimiters so that they aren't in the final docs - line = re.sub(language["multistart"],'',line) - line = re.sub(language["multiend"],'',line) + line = re.sub(re.escape(language["multistart"]),'',line) + line = re.sub(re.escape(language["multiend"]),'',line) docs_text += line.strip() + '\n' if has_code and docs_text.strip(): -- cgit v1.2.1 From 052ccc5c2c455a938ba97ad9e76e25c7b1c79283 Mon Sep 17 00:00:00 2001 From: Aaron Blohowiak Date: Mon, 13 Dec 2010 00:22:29 -0800 Subject: make JS work by escaping the start and end symbols --- pycco/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index 4daf745..67efe41 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -90,8 +90,8 @@ def parse(source, code): multi_line = False # Get rid of the delimiters so that they aren't in the final docs - line = re.sub(language["multistart"],'',line) - line = re.sub(language["multiend"],'',line) + line = re.sub(re.escape(language["multistart"]),'',line) + line = re.sub(re.escape(language["multiend"]),'',line) docs_text += line.strip() + '\n' if has_code and docs_text.strip(): -- cgit v1.2.1 From 9360e5974a95206d3f8143a04569ac1294aa4f44 Mon Sep 17 00:00:00 2001 From: Maximillian Dornseif Date: Fri, 21 Jan 2011 08:16:51 +0100 Subject: Fix CSS path in multi-dir mode --- pycco/main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index 67efe41..21a33c2 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -230,14 +230,14 @@ def generate_html(source, sections, preserve_paths=True, outdir=None): raise TypeError("Missing the required 'outdir' keyword argument") title = path.basename(source) dest = destination(source, preserve_paths=preserve_paths, outdir=outdir) + csspath = path.relpath(path.join(outdir, "pycco.css"), path.split(dest)[0]) for sect in sections: sect["code_html"] = re.sub(r"\{\{", r"__DOUBLE_OPEN_STACHE__", sect["code_html"]) rendered = pycco_template({ "title" : title, - "stylesheet" : path.relpath(path.join(path.dirname(dest), "pycco.css"), - path.split(dest)[0]), + "stylesheet" : csspath, "sections" : sections, "source" : source, "path" : path, @@ -388,7 +388,7 @@ def process(sources, preserve_paths=True, outdir=None): pass with open(destination(s, preserve_paths=preserve_paths, outdir=outdir), "w") as f: - f.write(generate_documentation(s, outdir=outdir)) + f.write(generate_documentation(s, preserve_paths=preserve_paths, outdir=outdir)) print "pycco = %s -> %s" % (s, dest) -- cgit v1.2.1 From 9b42946fae23601a755ca9d60161a0cf2d6c2eec Mon Sep 17 00:00:00 2001 From: Maximillian Dornseif Date: Fri, 21 Jan 2011 08:47:10 +0100 Subject: added a testcase --- testcases/python.py | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 testcases/python.py diff --git a/testcases/python.py b/testcases/python.py new file mode 100644 index 0000000..e225b18 --- /dev/null +++ b/testcases/python.py @@ -0,0 +1,9 @@ +import datetime + +# Comment 1 +class Class(object): + """Docstring 1""" + def get(self): + """Docstring 2""" + # Comment 2 - pycco puts the function into the documntation not the code + datum = datetime.date.today() -- cgit v1.2.1 From c4a5417b37cebcf14861071d42b6276eeaf93936 Mon Sep 17 00:00:00 2001 From: Michael Maltese Date: Mon, 11 Jul 2011 11:39:35 -0700 Subject: Don't give decorators their own separators --- pycco/main.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index 67efe41..e3e81ad 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -131,8 +131,9 @@ def parse(source, code): else: if code_text and any([line.lstrip().startswith(x) for x in ['class ', 'def ']]): - save(docs_text, code_text) - code_text = has_code = docs_text = '' + if not code_text.lstrip().startswith("@"): + save(docs_text, code_text) + code_text = has_code = docs_text = '' has_code = True code_text += line + '\n' -- cgit v1.2.1 From 519825ecefcb9a32038770ca13393a2a039e901f Mon Sep 17 00:00:00 2001 From: Michael Maltese Date: Mon, 11 Jul 2011 12:27:12 -0700 Subject: Allow sections with one empty section and one non-empty section (fixes commit bcb8f2c5f5a5adaee51e) --- pycco/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pycco/main.py b/pycco/main.py index e3e81ad..0a06b86 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -67,7 +67,7 @@ def parse(source, code): def save(docs, code): - if docs and code: + if docs or code: sections.append({ "docs_text": docs, "code_text": code -- cgit v1.2.1 From 92070bd6adf8997a3b3cbe53dc11e929acdf11f2 Mon Sep 17 00:00:00 2001 From: Michael Maltese Date: Mon, 11 Jul 2011 13:06:06 -0700 Subject: Create a new code section on decorators --- pycco/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pycco/main.py b/pycco/main.py index 0a06b86..fd91c65 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -130,7 +130,7 @@ def parse(source, code): docs_text += re.sub(language["comment_matcher"], "", line) + "\n" else: - if code_text and any([line.lstrip().startswith(x) for x in ['class ', 'def ']]): + if code_text and any([line.lstrip().startswith(x) for x in ['class ', 'def ', '@']]): if not code_text.lstrip().startswith("@"): save(docs_text, code_text) code_text = has_code = docs_text = '' @@ -140,6 +140,7 @@ def parse(source, code): save(docs_text, code_text) + return sections # === Preprocessing the comments === -- cgit v1.2.1 From a5f2327b8265eef4e88fe1a813259bcf8b620eba Mon Sep 17 00:00:00 2001 From: Michael Maltese Date: Tue, 12 Jul 2011 11:06:51 -0700 Subject: rstrip() code sections before highlighting -> generated docs looks tighter --- pycco/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pycco/main.py b/pycco/main.py index fd91c65..9ff3970 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -199,7 +199,7 @@ def highlight(source, sections, preserve_paths=True, outdir=None): raise TypeError("Missing the required 'outdir' keyword argument.") language = get_language(source) - output = pygments.highlight(language["divider_text"].join(section["code_text"] for section in sections), + output = pygments.highlight(language["divider_text"].join(section["code_text"].rstrip() for section in sections), language["lexer"], formatters.get_formatter_by_name("html")) -- cgit v1.2.1 From 16437f76ca63600e31a9e1cbe99008b60faf8dad Mon Sep 17 00:00:00 2001 From: Steve Johnson Date: Mon, 12 Sep 2011 18:24:13 -0300 Subject: Fix bad handling of comments that use multiline delimiters surrounding a single line --- pycco/main.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pycco/main.py b/pycco/main.py index 67efe41..d058fcb 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -88,6 +88,11 @@ def parse(source, code): else: multi_line = False + + if (multi_line + and line.strip().endswith(language.get("multiend")) + and len(line.strip()) > len(language.get("multiend"))): + multi_line = False # Get rid of the delimiters so that they aren't in the final docs line = re.sub(re.escape(language["multistart"]),'',line) -- cgit v1.2.1 From 347356715b691024ee2aac09e7221ec14056ef39 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Fri, 30 Sep 2011 13:27:16 -0700 Subject: Remove test case (accidently merged in #27) Removes test case added in fa778aab02f29a1371790088b58d3a5f70f233d1. --- testcases/python.py | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 testcases/python.py diff --git a/testcases/python.py b/testcases/python.py deleted file mode 100644 index e225b18..0000000 --- a/testcases/python.py +++ /dev/null @@ -1,9 +0,0 @@ -import datetime - -# Comment 1 -class Class(object): - """Docstring 1""" - def get(self): - """Docstring 2""" - # Comment 2 - pycco puts the function into the documntation not the code - datum = datetime.date.today() -- cgit v1.2.1 From de2046bfa366ba4d40e08e062c49b64b29bc1c20 Mon Sep 17 00:00:00 2001 From: Sebastian Hillig Date: Sat, 12 Mar 2011 10:45:18 -0800 Subject: Corrected typo in supported languages --- pycco/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pycco/main.py b/pycco/main.py index 98cd581..e3716de 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -294,7 +294,7 @@ languages = { "multistart": "#|", "multiend": "|#"}, ".lua": { "name": "lua", "symbol": "--", - "multistart": "--[[", "mutliend": "--]]"}, + "multistart": "--[[", "multiend": "--]]"}, ".erl": { "name": "erlang", "symbol": "%%" }, } -- cgit v1.2.1 From 335074ded48e08795b0176e3a4f5aeef2f307b24 Mon Sep 17 00:00:00 2001 From: Antti Kaihola Date: Mon, 3 Oct 2011 12:31:51 +0300 Subject: Added an option for continuous monitoring and automatic re-generating of documentation for changed source files --- pycco/main.py | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/pycco/main.py b/pycco/main.py index e3716de..6e211f5 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -263,6 +263,7 @@ import pygments import pystache import re import sys +import time from markdown import markdown from os import path from pygments import lexers, formatters @@ -376,7 +377,11 @@ def process(sources, preserve_paths=True, outdir=None): if not outdir: raise TypeError("Missing the required 'outdir' keyword argument.") - sources.sort() + # Make a copy of sources given on the command line. `main()` needs the + # original list when monitoring for changed files. + sources = sorted(sources) + + # Proceed to generating the documentation. if sources: ensure_directory(outdir) css = open(path.join(outdir, "pycco.css"), "w") @@ -403,6 +408,49 @@ def process(sources, preserve_paths=True, outdir=None): __all__ = ("process", "generate_documentation") + +# Monitor each source file and re-generate documentation on change. +def monitor(sources, opts): + # The watchdog modules are imported in `main()` but we need to re-import + # here to bring them into the local namespace. + import watchdog.events + import watchdog.observers + + # Watchdog operates on absolute paths, so map those to original paths + # as specified on the command line. + absolute_sources = dict((os.path.abspath(source), source) + for source in sources) + + class RegenerateHandler(watchdog.events.FileSystemEventHandler): + """A handler for recompiling files which triggered watchdog events""" + def on_modified(self, event): + """Regenerate documentation for a file which triggered an event""" + # Re-generate documentation from a source file if it was listed on + # the command line. Watchdog monitors whole directories, so other + # files may cause notifications as well. + if event.src_path in absolute_sources: + process([absolute_sources[event.src_path]], + outdir=opts.outdir, + preserve_paths=opts.paths) + + # Set up an observer which monitors all directories for files given on + # the command line and notifies the handler defined above. + event_handler = RegenerateHandler() + observer = watchdog.observers.Observer() + directories = set(os.path.split(source)[0] for source in sources) + for directory in directories: + observer.schedule(event_handler, path=directory) + + # Run the file change monitoring loop until the user hits Ctrl-C. + observer.start() + try: + while True: + time.sleep(1) + except KeyboardInterrupt: + observer.stop() + observer.join() + + # Hook spot for the console script. def main(): parser = optparse.OptionParser() @@ -413,9 +461,24 @@ def main(): dest='outdir', default='docs', help='The output directory that the rendered files should go to.') + parser.add_option('-w', '--watch', action='store_true', + help='Watch original files and re-generate documentation on changes') opts, sources = parser.parse_args() + process(sources, outdir=opts.outdir, preserve_paths=opts.paths) + # If the -w / --watch option was present, monitor the source directories + # for changes and re-generate documentation for source files whenever they + # are modified. + if opts.watch: + try: + import watchdog.events + import watchdog.observers + except ImportError: + sys.exit('The -w/--watch option requires the watchdog package.') + + monitor(sources, opts) + # Run the script. if __name__ == "__main__": main() -- cgit v1.2.1 From 5202c45baf121df52392819a785a49490693b102 Mon Sep 17 00:00:00 2001 From: Antti Kaihola Date: Mon, 3 Oct 2011 12:36:26 +0300 Subject: Added watchdog as an optional requirement (for the monitoring feature) in setup.py --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index c3bc074..b52da7d 100644 --- a/setup.py +++ b/setup.py @@ -16,4 +16,5 @@ setup( ] }, install_requires = ['markdown', 'pygments', 'pystache', 'smartypants'], + extras_require = {'monitoring': 'watchdog'}, ) -- cgit v1.2.1 From 6c52e0eb210dfcd51b408c24a45a1c485bfe313a Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Mon, 3 Oct 2011 08:08:17 -0700 Subject: Add link to Pycco source code on Github to docs --- pycco/main.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pycco/main.py b/pycco/main.py index eac2416..4bd6d86 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -# "**Pycco**" is a Python port of [Docco](http://jashkenas.github.com/docco/ ): +# "**Pycco**" is a Python port of [Docco](http://jashkenas.github.com/docco/): # the original quick-and-dirty, hundred-line-long, literate-programming-style # documentation generator. It produces HTML that displays your comments # alongside your code. Comments are passed through @@ -15,6 +15,9 @@ # # This will generate linked HTML documentation for the named source files, # saving it into a `docs` folder by default. + +# The [source for Pycco](https://github.com/fitzgen/pycco) is available on GitHub, +# and released under the MIT license. # # To install Pycco, simply # -- cgit v1.2.1 From c7fbcffd776449e2deca49d1e1ebf35dcddadd09 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Tue, 4 Oct 2011 08:32:38 -0700 Subject: Ignore trailing whitespace when finding sections --- pycco/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pycco/main.py b/pycco/main.py index 4bd6d86..915440e 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -189,7 +189,7 @@ def preprocess(comment, section_nr, preserve_paths=True, outdir=None): "name" : match.group(2) } - comment = re.sub('^([=]+)([^=]+)[=]*\\n', replace_section_name, comment) + comment = re.sub('^([=]+)([^=]+)[=]*\s*$', replace_section_name, comment) comment = re.sub('[^`]\[\[(.+)\]\]', replace_crossref, comment) return comment -- cgit v1.2.1 From 2e68f2b15ba67e5b77806fc566a6abcf81641730 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Thu, 6 Oct 2011 07:58:28 -0700 Subject: Use docstrings for methods, classes, and modules --- pycco/main.py | 171 +++++++++++++++++++++++++++++++++------------------------- 1 file changed, 99 insertions(+), 72 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index 915440e..58e1163 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -1,40 +1,45 @@ #!/usr/bin/env python -# "**Pycco**" is a Python port of [Docco](http://jashkenas.github.com/docco/): -# the original quick-and-dirty, hundred-line-long, literate-programming-style -# documentation generator. It produces HTML that displays your comments -# alongside your code. Comments are passed through -# [Markdown](http://daringfireball.net/projects/markdown/syntax) and -# [SmartyPants](http://daringfireball.net/projects/smartypants), while code is -# passed through [Pygments](http://pygments.org/) for syntax highlighting. -# This page is the result of running Pycco against its own source file. -# -# If you install Pycco, you can run it from the command-line: -# -# pycco src/*.py -# -# This will generate linked HTML documentation for the named source files, -# saving it into a `docs` folder by default. - -# The [source for Pycco](https://github.com/fitzgen/pycco) is available on GitHub, -# and released under the MIT license. -# -# To install Pycco, simply -# -# pip install pycco -# -# Or, to install the latest source -# -# git clone git://github.com/fitzgen/pycco.git -# cd pycco -# python setup.py install +""" +"**Pycco**" is a Python port of [Docco](http://jashkenas.github.com/docco/): +the original quick-and-dirty, hundred-line-long, literate-programming-style +documentation generator. It produces HTML that displays your comments +alongside your code. Comments are passed through +[Markdown](http://daringfireball.net/projects/markdown/syntax) and +[SmartyPants](http://daringfireball.net/projects/smartypants), while code is +passed through [Pygments](http://pygments.org/) for syntax highlighting. +This page is the result of running Pycco against its own source file. + +If you install Pycco, you can run it from the command-line: + + pycco src/*.py + +This will generate linked HTML documentation for the named source files, +saving it into a `docs` folder by default. + +The [source for Pycco](https://github.com/fitzgen/pycco) is available on GitHub, +and released under the MIT license. + +To install Pycco, simply + + pip install pycco + +Or, to install the latest source + + git clone git://github.com/fitzgen/pycco.git + cd pycco + python setup.py install +""" # === Main Documentation Generation Functions === -# Generate the documentation for a source file by reading it in, splitting it up -# into comment/code sections, highlighting them for the appropriate language, -# and merging them into an HTML template. def generate_documentation(source, outdir=None, preserve_paths=True): + """ + Generate the documentation for a source file by reading it in, splitting it + up into comment/code sections, highlighting them for the appropriate + language, and merging them into an HTML template. + """ + if not outdir: raise TypeError("Missing the required 'outdir' keyword argument.") fh = open(source, "r") @@ -42,18 +47,20 @@ def generate_documentation(source, outdir=None, preserve_paths=True): highlight(source, sections, preserve_paths=preserve_paths, outdir=outdir) return generate_html(source, sections, preserve_paths=preserve_paths, outdir=outdir) -# Given a string of source code, parse out each comment and the code that -# follows it, and create an individual **section** for it. -# Sections take the form: -# -# { "docs_text": ..., -# "docs_html": ..., -# "code_text": ..., -# "code_html": ..., -# "num": ... -# } -# def parse(source, code): + """ + Given a string of source code, parse out each comment and the code that + follows it, and create an individual **section** for it. + Sections take the form: + + { "docs_text": ..., + "docs_html": ..., + "code_text": ..., + "code_html": ..., + "num": ... + } + """ + lines = code.split("\n") sections = [] language = get_language(source) @@ -153,14 +160,17 @@ def parse(source, code): # === Preprocessing the comments === -# Add cross-references before having the text processed by markdown. It's -# possible to reference another file, like this : `[[main.py]]` which renders -# [[main.py]]. You can also reference a specific section of another file, like -# this: `[[main.py#highlighting-the-source-code]]` which renders as -# [[main.py#highlighting-the-source-code]]. Sections have to be manually -# declared; they are written on a single line, and surrounded by equals signs: -# `=== like this ===` def preprocess(comment, section_nr, preserve_paths=True, outdir=None): + """ + Add cross-references before having the text processed by markdown. It's + possible to reference another file, like this : `[[main.py]]` which renders + [[main.py]]. You can also reference a specific section of another file, like + this: `[[main.py#highlighting-the-source-code]]` which renders as + [[main.py#highlighting-the-source-code]]. Sections have to be manually + declared; they are written on a single line, and surrounded by equals signs: + `=== like this ===` + """ + if not outdir: raise TypeError("Missing the required 'outdir' keyword argument.") def sanitize_section_name(name): @@ -196,13 +206,16 @@ def preprocess(comment, section_nr, preserve_paths=True, outdir=None): # === Highlighting the source code === -# Highlights a single chunk of code using the **Pygments** module, and runs the -# text of its corresponding comment through **Markdown**. -# -# We process the entire file in a single call to Pygments by inserting little -# marker comments between each section and then splitting the result string -# wherever our markers occur. def highlight(source, sections, preserve_paths=True, outdir=None): + """ + Highlights a single chunk of code using the **Pygments** module, and runs + the text of its corresponding comment through **Markdown**. + + We process the entire file in a single call to Pygments by inserting little + marker comments between each section and then splitting the result string + wherever our markers occur. + """ + if not outdir: raise TypeError("Missing the required 'outdir' keyword argument.") language = get_language(source) @@ -227,15 +240,18 @@ def highlight(source, sections, preserve_paths=True, outdir=None): # === HTML Code generation === -# Once all of the code is finished highlighting, we can generate the HTML file -# and write out the documentation. Pass the completed sections into the template -# found in `resources/pycco.html`. -# -# Pystache will attempt to recursively render context variables, so we must -# replace any occurences of `{{`, which is valid in some languages, with a -# "unique enough" identifier before rendering, and then post-process the -# rendered template and change the identifier back to `{{`. def generate_html(source, sections, preserve_paths=True, outdir=None): + """ + Once all of the code is finished highlighting, we can generate the HTML file + and write out the documentation. Pass the completed sections into the + template found in `resources/pycco.html`. + + Pystache will attempt to recursively render context variables, so we must + replace any occurences of `{{`, which is valid in some languages, with a + "unique enough" identifier before rendering, and then post-process the + rendered template and change the identifier back to `{{`. + """ + if not outdir: raise TypeError("Missing the required 'outdir' keyword argument") title = path.basename(source) @@ -321,8 +337,9 @@ for ext, l in languages.items(): # Get the Pygments Lexer for this language. l["lexer"] = lexers.get_lexer_by_name(l["name"]) -# Get the current language we're documenting, based on the extension. def get_language(source): + """Get the current language we're documenting, based on the extension.""" + try: return languages[ source[source.rindex("."):] ] except ValueError: @@ -336,9 +353,12 @@ def get_language(source): else: raise ValueError("Can't figure out the language!") -# Compute the destination HTML path for an input source file path. If the source -# is `lib/example.py`, the HTML will be at `docs/example.html` def destination(filepath, preserve_paths=True, outdir=None): + """ + Compute the destination HTML path for an input source file path. If the + source is `lib/example.py`, the HTML will be at `docs/example.html` + """ + if not outdir: raise TypeError("Missing the required 'outdir' keyword argument.") try: @@ -349,16 +369,20 @@ def destination(filepath, preserve_paths=True, outdir=None): name = path.basename(name) return path.join(outdir, "%s.html" % name) -# Shift items off the front of the `list` until it is empty, then return -# `default`. def shift(list, default): + """ + Shift items off the front of the `list` until it is empty, then return + `default`. + """ + try: return list.pop(0) except IndexError: return default -# Ensure that the destination directory exists. def ensure_directory(directory): + """Ensure that the destination directory exists.""" + if not os.path.isdir(directory): os.mkdir(directory) @@ -377,8 +401,9 @@ highlight_start = "
"
 # The end of each Pygments highlight block.
 highlight_end = "
" -# For each source file passed in as an argument, generate the documentation. def process(sources, preserve_paths=True, outdir=None): + """For each source file passed as argument, generate the documentation.""" + if not outdir: raise TypeError("Missing the required 'outdir' keyword argument.") @@ -414,8 +439,9 @@ def process(sources, preserve_paths=True, outdir=None): __all__ = ("process", "generate_documentation") -# Monitor each source file and re-generate documentation on change. def monitor(sources, opts): + """Monitor each source file and re-generate documentation on change.""" + # The watchdog modules are imported in `main()` but we need to re-import # here to bring them into the local namespace. import watchdog.events @@ -456,8 +482,9 @@ def monitor(sources, opts): observer.join() -# Hook spot for the console script. def main(): + """Hook spot for the console script.""" + parser = optparse.OptionParser() parser.add_option('-p', '--paths', action='store_true', help='Preserve path structure of original files') -- cgit v1.2.1 From 3de4193cd7e2e12198bac28065670f7c6c9962b4 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Fri, 7 Oct 2011 08:01:42 -0700 Subject: Fix multiline code block indentation issue (#43) --- pycco/main.py | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index 58e1163..f954606 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -84,8 +84,7 @@ def parse(source, code): }) # Setup the variables to get ready to check for multiline comments - preformatted = multi_line = False - last_scope = 0 + multi_line = False multi_line_delimiters = [language.get("multistart"), language.get("multiend")] for line in lines: @@ -108,35 +107,16 @@ def parse(source, code): line = re.sub(re.escape(language["multistart"]),'',line) line = re.sub(re.escape(language["multiend"]),'',line) docs_text += line.strip() + '\n' + indent_level = re.match("\s*", line).group(0) if has_code and docs_text.strip(): save(docs_text, code_text[:-1]) code_text = code_text.split('\n')[-1] - last_scope = 0 has_code = docs_text = '' elif multi_line: - line_striped = line.rstrip() - current_scope = line_striped.count(" ") - - # This section will parse if the line is indented at least four - # places, and if so know to have the final text treat it as a - # preformatted text block. - if line_striped.startswith(" ") and last_scope: - if current_scope > last_scope and not preformatted: - preformatted = True - docs_text += "
"
-
-            else:
-                if preformatted:
-                    preformatted = False
-                    docs_text += "
" - - # Keep a tracker var to see if the scope increases, that way later - # the code can decided if a section is indented more than 4 spaces - # from the leading code. - last_scope = current_scope if current_scope > last_scope else last_scope - docs_text += line.strip() + '\n' + # Remove leading spaces + docs_text += line[len(indent_level):] + '\n' elif re.match(language["comment_matcher"], line): if has_code: -- cgit v1.2.1 From aa132916f703272b93fa448b08eb7a4fdfe01141 Mon Sep 17 00:00:00 2001 From: raylu Date: Sat, 17 Dec 2011 21:45:28 -0800 Subject: fix JS parsing error --- pycco/main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index f954606..75f7a3b 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -97,15 +97,15 @@ def parse(source, code): else: multi_line = False - + if (multi_line and line.strip().endswith(language.get("multiend")) and len(line.strip()) > len(language.get("multiend"))): multi_line = False # Get rid of the delimiters so that they aren't in the final docs - line = re.sub(re.escape(language["multistart"]),'',line) - line = re.sub(re.escape(language["multiend"]),'',line) + line = line.replace(language["multistart"], '') + line = line.replace(language["multiend"], '') docs_text += line.strip() + '\n' indent_level = re.match("\s*", line).group(0) -- cgit v1.2.1 From b3ff884430244f317b018c947ce308a3a98cad6e Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Fri, 23 Dec 2011 14:00:09 -0800 Subject: Only delete file extension when computing destination --- pycco/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pycco/main.py b/pycco/main.py index 75f7a3b..240845e 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -342,7 +342,7 @@ def destination(filepath, preserve_paths=True, outdir=None): if not outdir: raise TypeError("Missing the required 'outdir' keyword argument.") try: - name = filepath.replace(filepath[ filepath.rindex("."): ], "") + name = re.sub(r"\.[^.]*$", "", filepath) except ValueError: name = filepath if not preserve_paths: -- cgit v1.2.1 From 09f4dbfbc4adf7bf264d6a043f4df7c971db3168 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Thu, 29 Dec 2011 16:54:01 -0800 Subject: Bump version number (now 0.3.0) --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b52da7d..4e70456 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name = "Pycco", - version = "0.2.0", + version = "0.3.0", description = """A Python port of Docco: the original quick-and-dirty, hundred-line-long, literate-programming-style documentation generator. """, -- cgit v1.2.1 From 80224b2b183916c7641c76ef84d1905a2345dd08 Mon Sep 17 00:00:00 2001 From: raylu Date: Fri, 13 Jan 2012 18:15:55 -0800 Subject: handle unrecognized extensions and nested directories --- pycco/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index 240845e..49c5f83 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -322,7 +322,7 @@ def get_language(source): try: return languages[ source[source.rindex("."):] ] - except ValueError: + except KeyError: source = open(source, "r") code = source.read() source.close() @@ -364,7 +364,7 @@ def ensure_directory(directory): """Ensure that the destination directory exists.""" if not os.path.isdir(directory): - os.mkdir(directory) + os.makedirs(directory) def template(source): return lambda context: pystache.render(source, context) -- cgit v1.2.1 From 85079533f6775ad17fac8dcfc4ecae3639b7166b Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Tue, 31 Jan 2012 14:19:29 -0800 Subject: Remove only whitespaces for comment indentation --- pycco/main.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pycco/main.py b/pycco/main.py index 49c5f83..5817da4 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -116,7 +116,10 @@ def parse(source, code): elif multi_line: # Remove leading spaces - docs_text += line[len(indent_level):] + '\n' + if re.match(r' {%d}' % len(indent_level), line): + docs_text += line[len(indent_level):] + '\n' + else: + docs_text += line + '\n' elif re.match(language["comment_matcher"], line): if has_code: -- cgit v1.2.1 From 1b08a7f057c8d71edf7cb4523c32a8081e7ae55a Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Sun, 19 Feb 2012 08:50:07 -0800 Subject: Add EditorConfig file for indentation and newlines --- .editorconfig | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..fbee8fc --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +; This file is for unifying the coding style for different editors and IDEs. +; More information at http://EditorConfig.org + +root = true + +[*] +end_of_line = LF + +[*.py] +indent_style = space +indent_size = 4 + +[*.css] +indent_style = space +indent_size = 2 -- cgit v1.2.1 From f85ead9356087cdf2f52526dbef25eaa4f1e8e38 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Wed, 28 Mar 2012 07:13:41 -0700 Subject: Use non-greedy regex matching for [[hyperlinks]] --- pycco/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pycco/main.py b/pycco/main.py index 5817da4..8a3fba2 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -183,7 +183,7 @@ def preprocess(comment, section_nr, preserve_paths=True, outdir=None): } comment = re.sub('^([=]+)([^=]+)[=]*\s*$', replace_section_name, comment) - comment = re.sub('[^`]\[\[(.+)\]\]', replace_crossref, comment) + comment = re.sub('[^`]\[\[(.+?)\]\]', replace_crossref, comment) return comment -- cgit v1.2.1 From 42ba57ee19d9f28f05845911b589105d85e59016 Mon Sep 17 00:00:00 2001 From: Alec Perkins Date: Fri, 15 Jul 2011 12:02:12 -0400 Subject: Added support for CoffeeScript's multiline comments --- pycco/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pycco/main.py b/pycco/main.py index 8a3fba2..cdbb454 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -276,7 +276,8 @@ from pygments import lexers, formatters # the name of the Pygments lexer and the symbol that indicates a comment. To # add another language to Pycco's repertoire, add it here. languages = { - ".coffee": { "name": "coffee-script", "symbol": "#" }, + ".coffee": { "name": "coffee-script", "symbol": "#", + "multistart": '###', "multiend": '###' }, ".pl": { "name": "perl", "symbol": "#" }, -- cgit v1.2.1 From ea576162a54c6872cf2de87fb12a1f9f1c370814 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Sat, 31 Mar 2012 11:47:50 -0700 Subject: Generate doc filenames in a slightly smarter way Fixes issue #51 --- pycco/main.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index cdbb454..68d4823 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -324,9 +324,10 @@ for ext, l in languages.items(): def get_language(source): """Get the current language we're documenting, based on the extension.""" - try: - return languages[ source[source.rindex("."):] ] - except KeyError: + m = re.match(r'.*\.(.+)', os.path.basename(source)) + if m and languages.has_key(m.group(1)): + return languages[m.group(1)] + else: source = open(source, "r") code = source.read() source.close() @@ -343,14 +344,15 @@ def destination(filepath, preserve_paths=True, outdir=None): source is `lib/example.py`, the HTML will be at `docs/example.html` """ + dirname, filename = path.split(filepath) if not outdir: raise TypeError("Missing the required 'outdir' keyword argument.") try: - name = re.sub(r"\.[^.]*$", "", filepath) + name = re.sub(r"\.[^.]*$", "", filename) except ValueError: - name = filepath - if not preserve_paths: - name = path.basename(name) + name = filename + if preserve_paths: + name = path.join(dirname, name) return path.join(outdir, "%s.html" % name) def shift(list, default): @@ -411,7 +413,7 @@ def process(sources, preserve_paths=True, outdir=None): except OSError: pass - with open(destination(s, preserve_paths=preserve_paths, outdir=outdir), "w") as f: + with open(dest, "w") as f: f.write(generate_documentation(s, preserve_paths=preserve_paths, outdir=outdir)) print "pycco = %s -> %s" % (s, dest) -- cgit v1.2.1 From c0a6a23c954176e73572ff54cd6031a48aed0507 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Sat, 31 Mar 2012 11:59:07 -0700 Subject: Fix regular expression grouping (missing period) --- pycco/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index 68d4823..105a7cb 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -324,8 +324,8 @@ for ext, l in languages.items(): def get_language(source): """Get the current language we're documenting, based on the extension.""" - m = re.match(r'.*\.(.+)', os.path.basename(source)) - if m and languages.has_key(m.group(1)): + m = re.match(r'.*(\..+)', os.path.basename(source)) + if m and m.group(1) in languages: return languages[m.group(1)] else: source = open(source, "r") -- cgit v1.2.1 From 459f92f4956c793650dd582a68243899b2fc4b1f Mon Sep 17 00:00:00 2001 From: Lucas Jenss Date: Thu, 5 Apr 2012 15:31:17 +0200 Subject: Fix background not staying behind the code when scrolling horizontally This commit fixes a bug introduced in e2032e19b05 which changed the previous table layout to a pure CSS-based layout. Because the new layout used a "position: fixed" background, the background would not scroll when code lines were more wide than the browser window. This has been fixed by making the background "position: absolute" and moving it into the container DIV. Because the container DIV does not expand if its content is wider than the browser window though, I had to set the body background color to the same color than the code background and the container background to white, so that the left (comment) part stays white. --- pycco_resources/__init__.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pycco_resources/__init__.py b/pycco_resources/__init__.py index 71456b1..a052967 100644 --- a/pycco_resources/__init__.py +++ b/pycco_resources/__init__.py @@ -6,6 +6,7 @@ body { line-height: 24px; color: #252519; margin: 0; padding: 0; + background: #f5f5ff; } a { color: #261a3b; @@ -22,15 +23,18 @@ h1, h2, h3, h4, h5, h6 { h2, h3, h4, h5, h6 { margin-top: 0; } +#container { + background: white; + } #container, div.section { position: relative; } #background { - position: fixed; + position: absolute; top: 0; left: 580px; right: 0; bottom: 0; background: #f5f5ff; border-left: 1px solid #e5e5ee; - z-index: -1; + z-index: 0; } #jump_to, #jump_page { background: white; @@ -196,8 +200,8 @@ html = """\ -
+
{{#sources?}}
Jump To … -- cgit v1.2.1 From 048de0102ff971a14f4b0bf867482d42ed920de2 Mon Sep 17 00:00:00 2001 From: Avleen Vig Date: Fri, 25 May 2012 19:40:50 -0500 Subject: docstrings can occur at the end of lines, as well as the start - the PEP doesn't suggest that this shouldn't be done, and it's completely valid syntax which many people use. This patch allows the closing docstring triple-quote to happen at the end of a line.' --- pycco/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pycco/main.py b/pycco/main.py index 105a7cb..487ec5e 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -91,7 +91,7 @@ def parse(source, code): # Only go into multiline comments section when one of the delimiters is # found to be at the start of a line - if all(multi_line_delimiters) and any([line.lstrip().startswith(delim) for delim in multi_line_delimiters]): + if all(multi_line_delimiters) and any([line.lstrip().startswith(delim) or line.rstrip().endswith(delim) for delim in multi_line_delimiters]): if not multi_line: multi_line = True -- cgit v1.2.1 From a73aad95e666174a6fa5130378ff1a7f8ad63dc5 Mon Sep 17 00:00:00 2001 From: Benno Rice Date: Wed, 13 Jun 2012 11:53:07 +1000 Subject: Read in the code in the generate_documentation function. --- pycco/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index 487ec5e..f7b942e 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -42,8 +42,8 @@ def generate_documentation(source, outdir=None, preserve_paths=True): if not outdir: raise TypeError("Missing the required 'outdir' keyword argument.") - fh = open(source, "r") - sections = parse(source, fh.read()) + code = open(source, "r").read() + sections = parse(source, code) highlight(source, sections, preserve_paths=preserve_paths, outdir=outdir) return generate_html(source, sections, preserve_paths=preserve_paths, outdir=outdir) -- cgit v1.2.1 From cf540db2d25fe111fee502bf203f5bb192ddac50 Mon Sep 17 00:00:00 2001 From: Benno Rice Date: Wed, 13 Jun 2012 11:55:42 +1000 Subject: Move language detection up into generate_documentation so we only do it once. --- pycco/main.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index f7b942e..e027e60 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -43,11 +43,12 @@ def generate_documentation(source, outdir=None, preserve_paths=True): if not outdir: raise TypeError("Missing the required 'outdir' keyword argument.") code = open(source, "r").read() - sections = parse(source, code) - highlight(source, sections, preserve_paths=preserve_paths, outdir=outdir) + language = get_language(source, code) + sections = parse(source, code, language) + highlight(source, sections, language, preserve_paths=preserve_paths, outdir=outdir) return generate_html(source, sections, preserve_paths=preserve_paths, outdir=outdir) -def parse(source, code): +def parse(source, code, language): """ Given a string of source code, parse out each comment and the code that follows it, and create an individual **section** for it. @@ -63,7 +64,6 @@ def parse(source, code): lines = code.split("\n") sections = [] - language = get_language(source) has_code = docs_text = code_text = "" if lines[0].startswith("#!"): @@ -189,7 +189,7 @@ def preprocess(comment, section_nr, preserve_paths=True, outdir=None): # === Highlighting the source code === -def highlight(source, sections, preserve_paths=True, outdir=None): +def highlight(source, sections, language, preserve_paths=True, outdir=None): """ Highlights a single chunk of code using the **Pygments** module, and runs the text of its corresponding comment through **Markdown**. @@ -201,7 +201,6 @@ def highlight(source, sections, preserve_paths=True, outdir=None): if not outdir: raise TypeError("Missing the required 'outdir' keyword argument.") - language = get_language(source) output = pygments.highlight(language["divider_text"].join(section["code_text"].rstrip() for section in sections), language["lexer"], @@ -321,16 +320,13 @@ for ext, l in languages.items(): # Get the Pygments Lexer for this language. l["lexer"] = lexers.get_lexer_by_name(l["name"]) -def get_language(source): +def get_language(source, code): """Get the current language we're documenting, based on the extension.""" m = re.match(r'.*(\..+)', os.path.basename(source)) if m and m.group(1) in languages: return languages[m.group(1)] else: - source = open(source, "r") - code = source.read() - source.close() lang = lexers.guess_lexer(code).name.lower() for l in languages.values(): if l["name"] == lang: -- cgit v1.2.1 From 54ebc611fcce2e117b1a192c7bdf21047f9b7b48 Mon Sep 17 00:00:00 2001 From: Benno Rice Date: Wed, 13 Jun 2012 11:56:15 +1000 Subject: Allow language to be forced from the command line. Fixes #31. --- pycco/main.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index e027e60..0e30ab6 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -33,7 +33,8 @@ Or, to install the latest source # === Main Documentation Generation Functions === -def generate_documentation(source, outdir=None, preserve_paths=True): +def generate_documentation(source, outdir=None, preserve_paths=True, + language=None): """ Generate the documentation for a source file by reading it in, splitting it up into comment/code sections, highlighting them for the appropriate @@ -43,7 +44,7 @@ def generate_documentation(source, outdir=None, preserve_paths=True): if not outdir: raise TypeError("Missing the required 'outdir' keyword argument.") code = open(source, "r").read() - language = get_language(source, code) + language = get_language(source, code, language=language) sections = parse(source, code, language) highlight(source, sections, language, preserve_paths=preserve_paths, outdir=outdir) return generate_html(source, sections, preserve_paths=preserve_paths, outdir=outdir) @@ -320,9 +321,16 @@ for ext, l in languages.items(): # Get the Pygments Lexer for this language. l["lexer"] = lexers.get_lexer_by_name(l["name"]) -def get_language(source, code): +def get_language(source, code, language=None): """Get the current language we're documenting, based on the extension.""" + if language is not None: + for l in languages.values(): + if l["name"] == language: + return l + else: + raise ValueError("Unknown forced language: " + language) + m = re.match(r'.*(\..+)', os.path.basename(source)) if m and m.group(1) in languages: return languages[m.group(1)] @@ -383,7 +391,7 @@ highlight_start = "
"
 # The end of each Pygments highlight block.
 highlight_end = "
" -def process(sources, preserve_paths=True, outdir=None): +def process(sources, preserve_paths=True, outdir=None, language=None): """For each source file passed as argument, generate the documentation.""" if not outdir: @@ -410,7 +418,8 @@ def process(sources, preserve_paths=True, outdir=None): pass with open(dest, "w") as f: - f.write(generate_documentation(s, preserve_paths=preserve_paths, outdir=outdir)) + f.write(generate_documentation(s, preserve_paths=preserve_paths, outdir=outdir, + language=language)) print "pycco = %s -> %s" % (s, dest) @@ -477,9 +486,14 @@ def main(): parser.add_option('-w', '--watch', action='store_true', help='Watch original files and re-generate documentation on changes') + + parser.add_option('-l', '--force-language', action='store', type='string', + dest='language', default=None, + help='Force the language for the given files') opts, sources = parser.parse_args() - process(sources, outdir=opts.outdir, preserve_paths=opts.paths) + process(sources, outdir=opts.outdir, preserve_paths=opts.paths, + language=opts.language) # If the -w / --watch option was present, monitor the source directories # for changes and re-generate documentation for source files whenever they -- cgit v1.2.1 From 84fe8563b010dbe3ee9e2e4267d5697ca8b6fdcf Mon Sep 17 00:00:00 2001 From: Kazufumi Ohkawa Date: Thu, 21 Jun 2012 05:14:56 +0900 Subject: supported Haskell --- pycco/main.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pycco/main.py b/pycco/main.py index 0e30ab6..65c7c35 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -303,6 +303,9 @@ languages = { "multistart": "--[[", "multiend": "--]]"}, ".erl": { "name": "erlang", "symbol": "%%" }, + + ".hs": { "name": "haskell", "symbol": "--", + "multistart": "{-", "multiend": "-}"}, } # Build out the appropriate matchers and delimiters for each language. -- cgit v1.2.1 From 7e2d71230e882969a17d26501090c2495b4be5b0 Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Tue, 5 Mar 2013 12:23:27 -0800 Subject: Add language hook to support Tcl --- pycco/main.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pycco/main.py b/pycco/main.py index 65c7c35..1cfc4db 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -304,6 +304,8 @@ languages = { ".erl": { "name": "erlang", "symbol": "%%" }, + ".tcl": { "name": "tcl", "symbol": "#" }, + ".hs": { "name": "haskell", "symbol": "--", "multistart": "{-", "multiend": "-}"}, } -- cgit v1.2.1 From 1adb994be6202941fa3b85af9b525a7ab7642ba5 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Thu, 7 Mar 2013 22:17:17 -0800 Subject: Add CONTRIBUTING.md file noting project status --- CONTRIBUTING.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..4351196 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,12 @@ +# Contributing to Pycco + +This project currently lacks an active maintainer. + +The original author (Nick Fitzgerald) no longer maintains this project actively +and the current maintainer (Trey Hunner) no longer uses Pycco actively. + +## Help Us Out + +If you would like to help merge pull requests and manage issues email +Trey Hunner and Nick Fitzgerald so you can be granted access to manage the +repository. -- cgit v1.2.1 From c4e893fb3880e7840e15a3c3cc88efd7dedcc224 Mon Sep 17 00:00:00 2001 From: bryfry Date: Sat, 27 Apr 2013 01:18:02 -0300 Subject: add .c multiline --- pycco/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pycco/main.py b/pycco/main.py index 1cfc4db..09584b3 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -283,7 +283,8 @@ languages = { ".sql": { "name": "sql", "symbol": "--" }, - ".c": { "name": "c", "symbol": "//"}, + ".c": { "name": "c", "symbol": "//", + "multistart": "/*", "multiend": "*/"}, ".cpp": { "name": "cpp", "symbol": "//"}, -- cgit v1.2.1 From 056b1b75d984e8481341762b1b27c1ddcfb022db Mon Sep 17 00:00:00 2001 From: skampm2s Date: Fri, 21 Feb 2014 10:25:24 +0100 Subject: added opencl and improved some debug output --- pycco/main.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index 09584b3..8032201 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -288,6 +288,9 @@ languages = { ".cpp": { "name": "cpp", "symbol": "//"}, + ".cl": { "name": "c", "symbol": "//", + "multistart": "/*", "multiend": "*/"}, + ".js": { "name": "javascript", "symbol": "//", "multistart": "/*", "multiend": "*/"}, @@ -346,7 +349,7 @@ def get_language(source, code, language=None): if l["name"] == lang: return l else: - raise ValueError("Can't figure out the language!") + raise ValueError("Can't figure out the language! of %s" % source) def destination(filepath, preserve_paths=True, outdir=None): """ @@ -416,6 +419,7 @@ def process(sources, preserve_paths=True, outdir=None, language=None): def next_file(): s = sources.pop(0) + print "pycco = %s ->" % s, dest = destination(s, preserve_paths=preserve_paths, outdir=outdir) try: @@ -427,7 +431,7 @@ def process(sources, preserve_paths=True, outdir=None, language=None): f.write(generate_documentation(s, preserve_paths=preserve_paths, outdir=outdir, language=language)) - print "pycco = %s -> %s" % (s, dest) + print dest if sources: next_file() -- cgit v1.2.1 From 24641c22b84d3e2e478a9425a45f3e8b05b6e600 Mon Sep 17 00:00:00 2001 From: skampm2s Date: Fri, 21 Feb 2014 11:07:15 +0100 Subject: added c headers to available languages --- pycco/main.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pycco/main.py b/pycco/main.py index 8032201..547086c 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -286,6 +286,9 @@ languages = { ".c": { "name": "c", "symbol": "//", "multistart": "/*", "multiend": "*/"}, + ".h": { "name": "c", "symbol": "//", + "multistart": "/*", "multiend": "*/"}, + ".cpp": { "name": "cpp", "symbol": "//"}, ".cl": { "name": "c", "symbol": "//", -- cgit v1.2.1 From 8b5258d627a7f9269b7cf31341b4a5d953318f0f Mon Sep 17 00:00:00 2001 From: Steffen Kampmann Date: Sun, 23 Feb 2014 12:29:08 +0100 Subject: added optional index/sitemap generation --- pycco/main.py | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index 547086c..787a5d2 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -255,6 +255,59 @@ def generate_html(source, sections, preserve_paths=True, outdir=None): return re.sub(r"__DOUBLE_OPEN_STACHE__", "{{", rendered).encode("utf-8") +# === Sitemap Generation === +def generate_index(files, outdir): + + css_path = path.join(outdir, "pycco.css") + + sections = [] + + def add_file(entry, path, tree): + node, subpath = path[0], path[1:] + if not node in tree: + tree[node] = {} + + if subpath: + add_file(entry, subpath, tree[node]) + + else: + tree[node]['entry'] = entry + + tree = {} + for file_path in files: + entry = { + 'path': file_path, + 'relpath': path.relpath(file_path, outdir) + } + + add_file(entry=entry, path=entry['relpath'].split(path.sep), tree=tree) + + def generate_tree_html(tree): + items = [] + for node, subtree in tree.items(): + if 'entry' in subtree: + html = '
  • %s
  • ' % (subtree['entry']['relpath'], node) + + else: + html = '
    %s
      %s
    ' % (node, generate_tree_html(subtree)) + + items.append(html) + + return ''.join(items) + + sections.append({'docs_html': generate_tree_html(tree)}) + + rendered = pycco_template({ + "title" : 'Index', + "stylesheet" : css_path, + "sections" : sections, + "source" : '', + "path" : path, + "destination" : destination + }) + + return re.sub(r"__DOUBLE_OPEN_STACHE__", "{{", rendered).encode("utf-8") + # === Helpers & Setup === # This module contains all of our static resources. @@ -403,7 +456,7 @@ highlight_start = "
    "
     # The end of each Pygments highlight block.
     highlight_end = "
    " -def process(sources, preserve_paths=True, outdir=None, language=None): +def process(sources, preserve_paths=True, outdir=None, language=None, index=False): """For each source file passed as argument, generate the documentation.""" if not outdir: @@ -420,6 +473,8 @@ def process(sources, preserve_paths=True, outdir=None, language=None): css.write(pycco_styles) css.close() + generated_files = [] + def next_file(): s = sources.pop(0) print "pycco = %s ->" % s, @@ -433,13 +488,17 @@ def process(sources, preserve_paths=True, outdir=None, language=None): with open(dest, "w") as f: f.write(generate_documentation(s, preserve_paths=preserve_paths, outdir=outdir, language=language)) - print dest + generated_files.append(dest) if sources: next_file() next_file() + if index: + with open(path.join(outdir, "index.html"), "w") as f: + f.write(generate_index(generated_files, outdir)) + __all__ = ("process", "generate_documentation") @@ -503,10 +562,14 @@ def main(): parser.add_option('-l', '--force-language', action='store', type='string', dest='language', default=None, help='Force the language for the given files') + + parser.add_option('-i', '--generate_index', action='store_true', + help='Generate an index.html document with sitemap content') + opts, sources = parser.parse_args() process(sources, outdir=opts.outdir, preserve_paths=opts.paths, - language=opts.language) + language=opts.language, index=opts.generate_index) # If the -w / --watch option was present, monitor the source directories # for changes and re-generate documentation for source files whenever they -- cgit v1.2.1 From 8a55a6238fd0cccb69cb076e598c3ca806b22911 Mon Sep 17 00:00:00 2001 From: jonrsharpe Date: Mon, 19 Jan 2015 23:31:20 +0000 Subject: Handle comments that contain only cross-references --- pycco/main.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index 09584b3..77c7650 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -164,17 +164,17 @@ def preprocess(comment, section_nr, preserve_paths=True, outdir=None): # Check if the match contains an anchor if '#' in match.group(1): name, anchor = match.group(1).split('#') - return " [%s](%s#%s)" % (name, - path.basename(destination(name, - preserve_paths=preserve_paths, - outdir=outdir)), - anchor) + return "[%s](%s#%s)" % (name, + path.basename(destination(name, + preserve_paths=preserve_paths, + outdir=outdir)), + anchor) else: - return " [%s](%s)" % (match.group(1), - path.basename(destination(match.group(1), - preserve_paths=preserve_paths, - outdir=outdir))) + return "[%s](%s)" % (match.group(1), + path.basename(destination(match.group(1), + preserve_paths=preserve_paths, + outdir=outdir))) def replace_section_name(match): return '%(lvl)s %(name)s' % { @@ -184,7 +184,7 @@ def preprocess(comment, section_nr, preserve_paths=True, outdir=None): } comment = re.sub('^([=]+)([^=]+)[=]*\s*$', replace_section_name, comment) - comment = re.sub('[^`]\[\[(.+?)\]\]', replace_crossref, comment) + comment = re.sub('(? Date: Wed, 28 Oct 2015 21:35:44 -0400 Subject: (formatting) pep8 --- .gitignore | 3 ++- pycco/main.py | 82 ++++++++++++++++++++++++++++++++++------------------------- 2 files changed, 49 insertions(+), 36 deletions(-) diff --git a/.gitignore b/.gitignore index 07b20dd..d4f3e3a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ /Pycco.egg-info build/* dist/* -docs/* \ No newline at end of file +docs/* +/tags diff --git a/pycco/main.py b/pycco/main.py index 09584b3..e20430d 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -33,6 +33,7 @@ Or, to install the latest source # === Main Documentation Generation Functions === + def generate_documentation(source, outdir=None, preserve_paths=True, language=None): """ @@ -49,6 +50,7 @@ def generate_documentation(source, outdir=None, preserve_paths=True, highlight(source, sections, language, preserve_paths=preserve_paths, outdir=outdir) return generate_html(source, sections, preserve_paths=preserve_paths, outdir=outdir) + def parse(source, code, language): """ Given a string of source code, parse out each comment and the code that @@ -76,7 +78,6 @@ def parse(source, code, language): lines.pop(linenum) break - def save(docs, code): if docs or code: sections.append({ @@ -92,7 +93,8 @@ def parse(source, code, language): # Only go into multiline comments section when one of the delimiters is # found to be at the start of a line - if all(multi_line_delimiters) and any([line.lstrip().startswith(delim) or line.rstrip().endswith(delim) for delim in multi_line_delimiters]): + if all(multi_line_delimiters) and any([line.lstrip().startswith(delim) or + line.rstrip().endswith(delim) for delim in multi_line_delimiters]): if not multi_line: multi_line = True @@ -100,8 +102,8 @@ def parse(source, code, language): multi_line = False if (multi_line - and line.strip().endswith(language.get("multiend")) - and len(line.strip()) > len(language.get("multiend"))): + and line.strip().endswith(language.get("multiend")) + and len(line.strip()) > len(language.get("multiend"))): multi_line = False # Get rid of the delimiters so that they aren't in the final docs @@ -137,13 +139,13 @@ def parse(source, code, language): has_code = True code_text += line + '\n' - save(docs_text, code_text) return sections # === Preprocessing the comments === + def preprocess(comment, section_nr, preserve_paths=True, outdir=None): """ Add cross-references before having the text processed by markdown. It's @@ -157,6 +159,7 @@ def preprocess(comment, section_nr, preserve_paths=True, outdir=None): if not outdir: raise TypeError("Missing the required 'outdir' keyword argument.") + def sanitize_section_name(name): return "-".join(name.lower().strip().split(" ")) @@ -178,9 +181,9 @@ def preprocess(comment, section_nr, preserve_paths=True, outdir=None): def replace_section_name(match): return '%(lvl)s %(name)s' % { - "lvl" : re.sub('=', '#', match.group(1)), - "id" : sanitize_section_name(match.group(2)), - "name" : match.group(2) + "lvl": re.sub('=', '#', match.group(1)), + "id": sanitize_section_name(match.group(2)), + "name": match.group(2) } comment = re.sub('^([=]+)([^=]+)[=]*\s*$', replace_section_name, comment) @@ -190,6 +193,7 @@ def preprocess(comment, section_nr, preserve_paths=True, outdir=None): # === Highlighting the source code === + def highlight(source, sections, language, preserve_paths=True, outdir=None): """ Highlights a single chunk of code using the **Pygments** module, and runs @@ -223,6 +227,7 @@ def highlight(source, sections, language, preserve_paths=True, outdir=None): # === HTML Code generation === + def generate_html(source, sections, preserve_paths=True, outdir=None): """ Once all of the code is finished highlighting, we can generate the HTML file @@ -245,12 +250,12 @@ def generate_html(source, sections, preserve_paths=True, outdir=None): sect["code_html"] = re.sub(r"\{\{", r"__DOUBLE_OPEN_STACHE__", sect["code_html"]) rendered = pycco_template({ - "title" : title, - "stylesheet" : csspath, - "sections" : sections, - "source" : source, - "path" : path, - "destination" : destination + "title": title, + "stylesheet": csspath, + "sections": sections, + "source": source, + "path": path, + "destination": destination }) return re.sub(r"__DOUBLE_OPEN_STACHE__", "{{", rendered).encode("utf-8") @@ -276,39 +281,39 @@ from pygments import lexers, formatters # the name of the Pygments lexer and the symbol that indicates a comment. To # add another language to Pycco's repertoire, add it here. languages = { - ".coffee": { "name": "coffee-script", "symbol": "#", - "multistart": '###', "multiend": '###' }, + ".coffee": {"name": "coffee-script", "symbol": "#", + "multistart": '###', "multiend": '###'}, - ".pl": { "name": "perl", "symbol": "#" }, + ".pl": {"name": "perl", "symbol": "#"}, - ".sql": { "name": "sql", "symbol": "--" }, + ".sql": {"name": "sql", "symbol": "--"}, - ".c": { "name": "c", "symbol": "//", - "multistart": "/*", "multiend": "*/"}, + ".c": {"name": "c", "symbol": "//", + "multistart": "/*", "multiend": "*/"}, - ".cpp": { "name": "cpp", "symbol": "//"}, + ".cpp": {"name": "cpp", "symbol": "//"}, - ".js": { "name": "javascript", "symbol": "//", - "multistart": "/*", "multiend": "*/"}, + ".js": {"name": "javascript", "symbol": "//", + "multistart": "/*", "multiend": "*/"}, - ".rb": { "name": "ruby", "symbol": "#", - "multistart": "=begin", "multiend": "=end"}, + ".rb": {"name": "ruby", "symbol": "#", + "multistart": "=begin", "multiend": "=end"}, - ".py": { "name": "python", "symbol": "#", - "multistart": '"""', "multiend": '"""' }, + ".py": {"name": "python", "symbol": "#", + "multistart": '"""', "multiend": '"""' }, - ".scm": { "name": "scheme", "symbol": ";;", - "multistart": "#|", "multiend": "|#"}, + ".scm": {"name": "scheme", "symbol": ";;", + "multistart": "#|", "multiend": "|#"}, - ".lua": { "name": "lua", "symbol": "--", - "multistart": "--[[", "multiend": "--]]"}, + ".lua": {"name": "lua", "symbol": "--", + "multistart": "--[[", "multiend": "--]]"}, - ".erl": { "name": "erlang", "symbol": "%%" }, + ".erl": {"name": "erlang", "symbol": "%%"}, - ".tcl": { "name": "tcl", "symbol": "#" }, + ".tcl": {"name": "tcl", "symbol": "#"}, - ".hs": { "name": "haskell", "symbol": "--", - "multistart": "{-", "multiend": "-}"}, + ".hs": {"name": "haskell", "symbol": "--", + "multistart": "{-", "multiend": "-}"}, } # Build out the appropriate matchers and delimiters for each language. @@ -327,6 +332,7 @@ for ext, l in languages.items(): # Get the Pygments Lexer for this language. l["lexer"] = lexers.get_lexer_by_name(l["name"]) + def get_language(source, code, language=None): """Get the current language we're documenting, based on the extension.""" @@ -348,6 +354,7 @@ def get_language(source, code, language=None): else: raise ValueError("Can't figure out the language!") + def destination(filepath, preserve_paths=True, outdir=None): """ Compute the destination HTML path for an input source file path. If the @@ -365,6 +372,7 @@ def destination(filepath, preserve_paths=True, outdir=None): name = path.join(dirname, name) return path.join(outdir, "%s.html" % name) + def shift(list, default): """ Shift items off the front of the `list` until it is empty, then return @@ -376,12 +384,14 @@ def shift(list, default): except IndexError: return default + def ensure_directory(directory): """Ensure that the destination directory exists.""" if not os.path.isdir(directory): os.makedirs(directory) + def template(source): return lambda context: pystache.render(source, context) @@ -397,6 +407,7 @@ highlight_start = "
    "
     # The end of each Pygments highlight block.
     highlight_end = "
    " + def process(sources, preserve_paths=True, outdir=None, language=None): """For each source file passed as argument, generate the documentation.""" @@ -451,6 +462,7 @@ def monitor(sources, opts): class RegenerateHandler(watchdog.events.FileSystemEventHandler): """A handler for recompiling files which triggered watchdog events""" + def on_modified(self, event): """Regenerate documentation for a file which triggered an event""" # Re-generate documentation from a source file if it was listed on -- cgit v1.2.1 From a5c7c7809cd03313cbd0291511b52d6d35d94193 Mon Sep 17 00:00:00 2001 From: Andrew Trask Date: Tue, 3 Nov 2015 12:02:49 -0600 Subject: newline string bugfix in issue #17 --- pycco/main.py | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index 09584b3..6fd982f 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -86,16 +86,19 @@ def parse(source, code, language): # Setup the variables to get ready to check for multiline comments multi_line = False + multi_string = False multi_line_delimiters = [language.get("multistart"), language.get("multiend")] for line in lines: + + process_as_code = False # Only go into multiline comments section when one of the delimiters is # found to be at the start of a line if all(multi_line_delimiters) and any([line.lstrip().startswith(delim) or line.rstrip().endswith(delim) for delim in multi_line_delimiters]): if not multi_line: multi_line = True - + else: multi_line = False @@ -104,16 +107,27 @@ def parse(source, code, language): and len(line.strip()) > len(language.get("multiend"))): multi_line = False - # Get rid of the delimiters so that they aren't in the final docs - line = line.replace(language["multistart"], '') - line = line.replace(language["multiend"], '') - docs_text += line.strip() + '\n' - indent_level = re.match("\s*", line).group(0) + if((not line.strip().startswith(language.get("multistart")) and not multi_line) or multi_string): + + process_as_code = True + + if(multi_string): + multi_line = False + multi_string = False + else: + multi_string = True - if has_code and docs_text.strip(): - save(docs_text, code_text[:-1]) - code_text = code_text.split('\n')[-1] - has_code = docs_text = '' + else: + # Get rid of the delimiters so that they aren't in the final docs + line = line.replace(language["multistart"], '') + line = line.replace(language["multiend"], '') + docs_text += line.strip() + '\n' + indent_level = re.match("\s*", line).group(0) + + if has_code and docs_text.strip(): + save(docs_text, code_text[:-1]) + code_text = code_text.split('\n')[-1] + has_code = docs_text = '' elif multi_line: # Remove leading spaces @@ -129,13 +143,16 @@ def parse(source, code, language): docs_text += re.sub(language["comment_matcher"], "", line) + "\n" else: + process_as_code = True + + if(process_as_code): if code_text and any([line.lstrip().startswith(x) for x in ['class ', 'def ', '@']]): if not code_text.lstrip().startswith("@"): save(docs_text, code_text) code_text = has_code = docs_text = '' has_code = True - code_text += line + '\n' + code_text += line + '\n' save(docs_text, code_text) -- cgit v1.2.1 From 1fc58dd74503e3346314a51c9427de1a1228414c Mon Sep 17 00:00:00 2001 From: zax Date: Wed, 28 Oct 2015 21:36:05 -0400 Subject: Requirements, cleanup and tests --- .gitignore | 6 +++ pycco/main.py | 94 +++++++++++++++++++++++++---------------------- pycco/tests/__init__.py | 0 pycco/tests/test_pycco.py | 29 +++++++++++++++ requirements.txt | 2 + 5 files changed, 87 insertions(+), 44 deletions(-) create mode 100644 pycco/tests/__init__.py create mode 100644 pycco/tests/test_pycco.py create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore index d4f3e3a..5306a68 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,9 @@ build/* dist/* docs/* /tags + +.cache +.hypothesis +.ropeproject + +.DS_Store diff --git a/pycco/main.py b/pycco/main.py index e20430d..6b8abac 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -46,12 +46,12 @@ def generate_documentation(source, outdir=None, preserve_paths=True, raise TypeError("Missing the required 'outdir' keyword argument.") code = open(source, "r").read() language = get_language(source, code, language=language) - sections = parse(source, code, language) - highlight(source, sections, language, preserve_paths=preserve_paths, outdir=outdir) + sections = parse(code, language) + highlight(sections, language, preserve_paths=preserve_paths, outdir=outdir) return generate_html(source, sections, preserve_paths=preserve_paths, outdir=outdir) -def parse(source, code, language): +def parse(code, language): """ Given a string of source code, parse out each comment and the code that follows it, and create an individual **section** for it. @@ -78,7 +78,7 @@ def parse(source, code, language): lines.pop(linenum) break - def save(docs, code): + def save(docs, code, sections): if docs or code: sections.append({ "docs_text": docs, @@ -87,66 +87,64 @@ def parse(source, code, language): # Setup the variables to get ready to check for multiline comments multi_line = False - multi_line_delimiters = [language.get("multistart"), language.get("multiend")] + multistart, multiend = [language.get("multistart"), language.get("multiend")] + comment_matcher = language['comment_matcher'] for line in lines: - # Only go into multiline comments section when one of the delimiters is # found to be at the start of a line - if all(multi_line_delimiters) and any([line.lstrip().startswith(delim) or - line.rstrip().endswith(delim) for delim in multi_line_delimiters]): - if not multi_line: - multi_line = True - - else: - multi_line = False + if multistart and multiend and \ + any(line.lstrip().startswith(delim) or line.rstrip().endswith(delim) + for delim in (multistart, multiend)): + multi_line = not multi_line if (multi_line - and line.strip().endswith(language.get("multiend")) - and len(line.strip()) > len(language.get("multiend"))): + and line.strip().endswith(multiend) + and len(line.strip()) > len(multiend)): multi_line = False # Get rid of the delimiters so that they aren't in the final docs - line = line.replace(language["multistart"], '') - line = line.replace(language["multiend"], '') + line = line.replace(multistart, '') + line = line.replace(multiend, '') docs_text += line.strip() + '\n' indent_level = re.match("\s*", line).group(0) if has_code and docs_text.strip(): - save(docs_text, code_text[:-1]) + save(docs_text, code_text[:-1], sections) code_text = code_text.split('\n')[-1] has_code = docs_text = '' elif multi_line: # Remove leading spaces - if re.match(r' {%d}' % len(indent_level), line): + if re.match(r' {:d}'.format(len(indent_level), line)): docs_text += line[len(indent_level):] + '\n' else: docs_text += line + '\n' - elif re.match(language["comment_matcher"], line): + elif re.match(comment_matcher, line): if has_code: - save(docs_text, code_text) + save(docs_text, code_text, sections) has_code = docs_text = code_text = '' - docs_text += re.sub(language["comment_matcher"], "", line) + "\n" + docs_text += re.sub(comment_matcher, "", line) + "\n" else: - if code_text and any([line.lstrip().startswith(x) for x in ['class ', 'def ', '@']]): + if code_text and any(line.lstrip().startswith(x) + for x in ['class ', 'def ', '@']): if not code_text.lstrip().startswith("@"): - save(docs_text, code_text) + save(docs_text, code_text, sections) code_text = has_code = docs_text = '' has_code = True code_text += line + '\n' - save(docs_text, code_text) + save(docs_text, code_text, sections) return sections # === Preprocessing the comments === -def preprocess(comment, section_nr, preserve_paths=True, outdir=None): +def preprocess(comment, preserve_paths=True, outdir=None): """ Add cross-references before having the text processed by markdown. It's possible to reference another file, like this : `[[main.py]]` which renders @@ -167,24 +165,27 @@ def preprocess(comment, section_nr, preserve_paths=True, outdir=None): # Check if the match contains an anchor if '#' in match.group(1): name, anchor = match.group(1).split('#') - return " [%s](%s#%s)" % (name, - path.basename(destination(name, - preserve_paths=preserve_paths, - outdir=outdir)), - anchor) + return " [{}]({}#{})".format(name, + path.basename(destination(name, + preserve_paths=preserve_paths, + outdir=outdir)), + anchor) else: - return " [%s](%s)" % (match.group(1), - path.basename(destination(match.group(1), - preserve_paths=preserve_paths, - outdir=outdir))) + return " [{}]({})".format(match.group(1), + path.basename(destination(match.group(1), + preserve_paths=preserve_paths, + outdir=outdir))) def replace_section_name(match): - return '%(lvl)s %(name)s' % { - "lvl": re.sub('=', '#', match.group(1)), - "id": sanitize_section_name(match.group(2)), - "name": match.group(2) - } + """ + Replace equals-sign-formatted section names with anchor links. + """ + return '{lvl} {name}'.format( + lvl=re.sub('=', '#', match.group(1)), + id=sanitize_section_name(match.group(2)), + name=match.group(2) + ) comment = re.sub('^([=]+)([^=]+)[=]*\s*$', replace_section_name, comment) comment = re.sub('[^`]\[\[(.+?)\]\]', replace_crossref, comment) @@ -194,7 +195,7 @@ def preprocess(comment, section_nr, preserve_paths=True, outdir=None): # === Highlighting the source code === -def highlight(source, sections, language, preserve_paths=True, outdir=None): +def highlight(sections, language, preserve_paths=True, outdir=None): """ Highlights a single chunk of code using the **Pygments** module, and runs the text of its corresponding comment through **Markdown**. @@ -220,7 +221,6 @@ def highlight(source, sections, language, preserve_paths=True, outdir=None): except UnicodeError: docs_text = unicode(section["docs_text"].decode('utf-8')) section["docs_html"] = markdown(preprocess(docs_text, - i, preserve_paths=preserve_paths, outdir=outdir)) section["num"] = i @@ -370,7 +370,13 @@ def destination(filepath, preserve_paths=True, outdir=None): name = filename if preserve_paths: name = path.join(dirname, name) - return path.join(outdir, "%s.html" % name) + dest = path.join(outdir, u"{}.html".format(name)) + # If `join` is passed an absolute path, it will ignore any earlier path + # elements. We will force outdir to the beginning of the path to avoid + # writing outside our destination. + if not dest.startswith(outdir): + dest = outdir + os.sep + dest + return dest def shift(list, default): @@ -438,7 +444,7 @@ def process(sources, preserve_paths=True, outdir=None, language=None): f.write(generate_documentation(s, preserve_paths=preserve_paths, outdir=outdir, language=language)) - print "pycco = %s -> %s" % (s, dest) + print "pycco = {} -> {}".format(s, dest) if sources: next_file() diff --git a/pycco/tests/__init__.py b/pycco/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycco/tests/test_pycco.py b/pycco/tests/test_pycco.py new file mode 100644 index 0000000..43abe36 --- /dev/null +++ b/pycco/tests/test_pycco.py @@ -0,0 +1,29 @@ +from hypothesis import given +from hypothesis.strategies import lists, text, booleans, integers +import pycco.main as p +import copy + + +@given(lists(text()), text()) +def test_shift(fragments, default): + if fragments == []: + assert p.shift(fragments, default) == default + else: + fragments2 = copy.copy(fragments) + head = p.shift(fragments, default) + assert [head] + fragments == fragments2 + + +@given(text(), booleans(), text(min_size=1)) +def test_destination(filepath, preserve_paths, outdir): + dest = p.destination(filepath, preserve_paths=preserve_paths, outdir=outdir) + assert dest.startswith(outdir) + assert dest.endswith(".html") + + +@given(integers(min_value=0, max_value=12), text()) +def test_parse(n, source): + languages = p.languages + l = languages[languages.keys()[n]] + parsed = p.parse(source, l) + assert [{"code_text", "docs_text"} == set(s.keys()) for s in parsed] diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b2e7043 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +pystache==0.5.4 +markdown==2.6.3 -- cgit v1.2.1 From 29e059bccc2206f927d1a3ca348ed04ae2b1a17e Mon Sep 17 00:00:00 2001 From: zax Date: Sun, 1 Nov 2015 12:46:54 -0500 Subject: Basic CI with Travis and Coveralls. --- .gitignore | 1 + .travis.yml | 10 +++++ README | 25 ----------- README.md | 30 +++++++++++++ pycco/main.py | 36 +++++++++++----- pycco/tests/__init__.py | 0 pycco/tests/test_pycco.py | 29 ------------- requirements.test.txt | 3 ++ requirements.txt | 1 + tests/__init__.py | 0 tests/test_pycco.py | 107 ++++++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 177 insertions(+), 65 deletions(-) create mode 100644 .travis.yml delete mode 100644 README create mode 100644 README.md delete mode 100644 pycco/tests/__init__.py delete mode 100644 pycco/tests/test_pycco.py create mode 100644 requirements.test.txt create mode 100644 tests/__init__.py create mode 100644 tests/test_pycco.py diff --git a/.gitignore b/.gitignore index 5306a68..e1c9655 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.coverage *.pyc /Pycco.egg-info build/* diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..bfbe563 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +language: python +python: + - '2.7' +install: + - 'pip install -r requirements.txt' + - 'pip install -r requirements.test.txt' +script: + - 'py.test --cov=pycco tests/' +after_success: + - coveralls diff --git a/README b/README deleted file mode 100644 index 5343728..0000000 --- a/README +++ /dev/null @@ -1,25 +0,0 @@ -888888b. -888 Y88b -888 888 -888 d88P 888 888 .d8888b .d8888b .d88b. -8888888P" 888 888 d88P" d88P" d88""88b -888 888 888 888 888 888 888 -888 Y88b 888 Y88b. Y88b. Y88..88P -888 "Y88888 "Y8888P "Y8888P "Y88P" - 888 - Y8b d88P - "Y88P" - -Pycco is a Python port of Docco: the original quick-and-dirty, hundred-line- -long, literate-programming-style documentation generator. For more information, -see: - -http://fitzgen.github.com/pycco/ - -Others: - -CoffeeScript (Original) - http://jashkenas.github.com/docco/ - -Ruby - http://rtomayko.github.com/rocco/ - -Sh - http://rtomayko.github.com/shocco/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..9cad492 --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +``` +888888b. +888 Y88b +888 888 +888 d88P 888 888 .d8888b .d8888b .d88b. +8888888P" 888 888 d88P" d88P" d88""88b +888 888 888 888 888 888 888 +888 Y88b 888 Y88b. Y88b. Y88..88P +888 "Y88888 "Y8888P "Y8888P "Y88P" + 888 + Y8b d88P + "Y88P" +``` + +Pycco is a Python port of Docco: the original quick-and-dirty, hundred-line- +long, literate-programming-style documentation generator. For more information, +see: + +http://fitzgen.github.com/pycco/ + +Others: + +CoffeeScript (Original) - http://jashkenas.github.com/docco/ + +Ruby - http://rtomayko.github.com/rocco/ + +Sh - http://rtomayko.github.com/shocco/ + +[![Build Status](https://travis-ci.org/subsetpark/pycco.svg?branch=hypothesis)](https://travis-ci.org/subsetpark/pycco) +[![Coverage Status](https://coveralls.io/repos/subsetpark/pycco/badge.svg?branch=hypothesis&service=github)](https://coveralls.io/github/subsetpark/pycco?branch=hypothesis) diff --git a/pycco/main.py b/pycco/main.py index 6b8abac..20e5a6b 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -116,7 +116,7 @@ def parse(code, language): elif multi_line: # Remove leading spaces - if re.match(r' {:d}'.format(len(indent_level), line)): + if re.match(r' {:d}'.format(len(indent_level)), line): docs_text += line[len(indent_level):] + '\n' else: docs_text += line + '\n' @@ -300,7 +300,7 @@ languages = { "multistart": "=begin", "multiend": "=end"}, ".py": {"name": "python", "symbol": "#", - "multistart": '"""', "multiend": '"""' }, + "multistart": '"""', "multiend": '"""'}, ".scm": {"name": "scheme", "symbol": ";;", "multistart": "#|", "multiend": "|#"}, @@ -343,15 +343,21 @@ def get_language(source, code, language=None): else: raise ValueError("Unknown forced language: " + language) - m = re.match(r'.*(\..+)', os.path.basename(source)) + m = re.match(r'.*(\..+)', os.path.basename(source)) if source else None if m and m.group(1) in languages: return languages[m.group(1)] else: - lang = lexers.guess_lexer(code).name.lower() - for l in languages.values(): - if l["name"] == lang: - return l - else: + try: + lang = lexers.guess_lexer(code).name.lower() + for l in languages.values(): + if l["name"] == lang: + return l + else: + raise ValueError() + except ValueError: + # If pygments can't find any lexers, it will raise its own + # subclass of ValueError. We will catch it and raise ours + # for consistency. raise ValueError("Can't figure out the language!") @@ -392,11 +398,19 @@ def shift(list, default): def ensure_directory(directory): - """Ensure that the destination directory exists.""" - + """ + Sanitize directory string and ensure that the destination directory exists. + """ + # Sanitization regexp copied from + # http://stackoverflow.com/questions/92438/stripping-non-printable-characters-from-a-string-in-python + control_chars = ''.join(map(unichr, range(0, 32) + range(127, 160))) + control_char_re = re.compile(u'[{}]'.format(re.escape(control_chars))) + directory = control_char_re.sub('', directory) if not os.path.isdir(directory): os.makedirs(directory) + return directory + def template(source): return lambda context: pystache.render(source, context) @@ -426,7 +440,7 @@ def process(sources, preserve_paths=True, outdir=None, language=None): # Proceed to generating the documentation. if sources: - ensure_directory(outdir) + outdir = ensure_directory(outdir) css = open(path.join(outdir, "pycco.css"), "w") css.write(pycco_styles) css.close() diff --git a/pycco/tests/__init__.py b/pycco/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/pycco/tests/test_pycco.py b/pycco/tests/test_pycco.py deleted file mode 100644 index 43abe36..0000000 --- a/pycco/tests/test_pycco.py +++ /dev/null @@ -1,29 +0,0 @@ -from hypothesis import given -from hypothesis.strategies import lists, text, booleans, integers -import pycco.main as p -import copy - - -@given(lists(text()), text()) -def test_shift(fragments, default): - if fragments == []: - assert p.shift(fragments, default) == default - else: - fragments2 = copy.copy(fragments) - head = p.shift(fragments, default) - assert [head] + fragments == fragments2 - - -@given(text(), booleans(), text(min_size=1)) -def test_destination(filepath, preserve_paths, outdir): - dest = p.destination(filepath, preserve_paths=preserve_paths, outdir=outdir) - assert dest.startswith(outdir) - assert dest.endswith(".html") - - -@given(integers(min_value=0, max_value=12), text()) -def test_parse(n, source): - languages = p.languages - l = languages[languages.keys()[n]] - parsed = p.parse(source, l) - assert [{"code_text", "docs_text"} == set(s.keys()) for s in parsed] diff --git a/requirements.test.txt b/requirements.test.txt new file mode 100644 index 0000000..8439fc2 --- /dev/null +++ b/requirements.test.txt @@ -0,0 +1,3 @@ +hypothesis==1.14.0 +pytest-cov==2.2.0 +coveralls==1.1 diff --git a/requirements.txt b/requirements.txt index b2e7043..38964da 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ pystache==0.5.4 +Pygments==2.0.2 markdown==2.6.3 diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_pycco.py b/tests/test_pycco.py new file mode 100644 index 0000000..04cb57e --- /dev/null +++ b/tests/test_pycco.py @@ -0,0 +1,107 @@ +import copy +import tempfile +import pytest +import os +import re +from hypothesis import given, example, assume +from hypothesis.strategies import lists, text, booleans, choices, none + +import pycco.main as p + +PYTHON = p.languages['.py'] +PYCCO_SOURCE = 'pycco/main.py' +FOO_FUNCTION = """def foo():\n return True""" + + +@given(lists(text()), text()) +def test_shift(fragments, default): + if fragments == []: + assert p.shift(fragments, default) == default + else: + fragments2 = copy.copy(fragments) + head = p.shift(fragments, default) + assert [head] + fragments == fragments2 + + +@given(text(), booleans(), text(min_size=1)) +@example("/foo", True, "0") +def test_destination(filepath, preserve_paths, outdir): + dest = p.destination(filepath, preserve_paths=preserve_paths, outdir=outdir) + assert dest.startswith(outdir) + assert dest.endswith(".html") + + +@given(choices(), text()) +def test_parse(choice, source): + l = choice(p.languages.values()) + parsed = p.parse(source, l) + assert [{"code_text", "docs_text"} == set(s.keys()) for s in parsed] + + +def test_skip_coding_directive(): + source = "# -*- coding: utf-8 -*-\n" + FOO_FUNCTION + parsed = p.parse(source, PYTHON) + for section in parsed: + assert "coding" not in section['code_text'] + + +def test_multi_line_leading_spaces(): + source = "# This is a\n# comment that\n# is indented\n" + source += FOO_FUNCTION + parsed = p.parse(source, PYTHON) + # The resulting comment has leading spaces stripped out. + assert parsed[0]["docs_text"] == "This is a\ncomment that\nis indented\n" + + +@given(text(), text()) +def test_get_language_specify_language(source, code): + assert p.get_language(source, code, language="python") == p.languages['.py'] + + with pytest.raises(ValueError): + p.get_language(source, code, language="non-existent") + + +@given(text() | none()) +def test_get_language_bad_source(source): + code = "#!/usr/bin/python\n" + code += FOO_FUNCTION + assert p.get_language(source, code) == PYTHON + with pytest.raises(ValueError) as e: + assert p.get_language(source, "badlang") + + assert e.value.message == "Can't figure out the language!" + + +@given(text() | none()) +def test_get_language_bad_code(code): + source = "test.py" + assert p.get_language(source, code) == PYTHON + + +@given(text(max_size=64)) +def test_ensure_directory(dir_name): + tempdir = os.path.join(tempfile.gettempdir(), dir_name) + + # Copy and paste sanitization from function, but only for housekeeping. We + # pass in the unsanitized string to the function. + control_chars = ''.join(map(unichr, range(0, 32) + range(127, 160))) + control_char_re = re.compile(u'[{}]'.format(re.escape(control_chars))) + safe_name = control_char_re.sub('', tempdir) + + if not os.path.isdir(safe_name): + assume(os.access(safe_name, os.W_OK)) + p.ensure_directory(tempdir) + assert os.path.isdir(safe_name) + +# The following functions get good test coverage, but effort should be put into +# decomposing the functions they test and actually testing their output. + + +def test_generate_documentation(): + p.generate_documentation(PYCCO_SOURCE, outdir=tempfile.gettempdir()) + + +@given(booleans(), choices()) +def test_process(preserve_paths, choice): + lang_name = choice([l["name"] for l in p.languages.values()]) + p.process([PYCCO_SOURCE], preserve_paths=preserve_paths, outdir=tempfile.gettempdir(), language=lang_name) -- cgit v1.2.1 From 32010829e4c1f2ca43eef7e5ba197daa8cfaad1e Mon Sep 17 00:00:00 2001 From: zax Date: Sun, 1 Nov 2015 16:29:49 -0500 Subject: Factor out file handling in generate_documentation --- pycco/main.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index 20e5a6b..df2b2bc 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -45,10 +45,17 @@ def generate_documentation(source, outdir=None, preserve_paths=True, if not outdir: raise TypeError("Missing the required 'outdir' keyword argument.") code = open(source, "r").read() - language = get_language(source, code, language=language) + return _generate_documentation(source, code, outdir, preserve_paths, language) + + +def _generate_documentation(file_path, code, outdir, preserve_paths, language): + """ + Helper function to allow documentation generation without file handling. + """ + language = get_language(file_path, code, language=language) sections = parse(code, language) highlight(sections, language, preserve_paths=preserve_paths, outdir=outdir) - return generate_html(source, sections, preserve_paths=preserve_paths, outdir=outdir) + return generate_html(file_path, sections, preserve_paths=preserve_paths, outdir=outdir) def parse(code, language): @@ -56,7 +63,6 @@ def parse(code, language): Given a string of source code, parse out each comment and the code that follows it, and create an individual **section** for it. Sections take the form: - { "docs_text": ..., "docs_html": ..., "code_text": ..., @@ -78,7 +84,7 @@ def parse(code, language): lines.pop(linenum) break - def save(docs, code, sections): + def save(docs, code): if docs or code: sections.append({ "docs_text": docs, @@ -87,14 +93,14 @@ def parse(code, language): # Setup the variables to get ready to check for multiline comments multi_line = False - multistart, multiend = [language.get("multistart"), language.get("multiend")] + multistart, multiend = language.get("multistart"), language.get("multiend") comment_matcher = language['comment_matcher'] for line in lines: # Only go into multiline comments section when one of the delimiters is # found to be at the start of a line if multistart and multiend and \ - any(line.lstrip().startswith(delim) or line.rstrip().endswith(delim) + any(line.lstrip().startswith(delim) or line.rstrip().endswith(delim) for delim in (multistart, multiend)): multi_line = not multi_line @@ -110,20 +116,20 @@ def parse(code, language): indent_level = re.match("\s*", line).group(0) if has_code and docs_text.strip(): - save(docs_text, code_text[:-1], sections) + save(docs_text, code_text[:-1]) code_text = code_text.split('\n')[-1] has_code = docs_text = '' elif multi_line: # Remove leading spaces - if re.match(r' {:d}'.format(len(indent_level)), line): + if re.match(r' {{{:d}}}'.format(len(indent_level)), line): docs_text += line[len(indent_level):] + '\n' else: docs_text += line + '\n' elif re.match(comment_matcher, line): if has_code: - save(docs_text, code_text, sections) + save(docs_text, code_text) has_code = docs_text = code_text = '' docs_text += re.sub(comment_matcher, "", line) + "\n" @@ -131,13 +137,13 @@ def parse(code, language): if code_text and any(line.lstrip().startswith(x) for x in ['class ', 'def ', '@']): if not code_text.lstrip().startswith("@"): - save(docs_text, code_text, sections) + save(docs_text, code_text) code_text = has_code = docs_text = '' has_code = True code_text += line + '\n' - save(docs_text, code_text, sections) + save(docs_text, code_text) return sections -- cgit v1.2.1 From 4f82bd2d8fb796c746680c1d1e8d5f99a1cd18fd Mon Sep 17 00:00:00 2001 From: zax Date: Sun, 1 Nov 2015 16:31:47 -0500 Subject: Basic Python 3 support --- .travis.yml | 2 ++ pycco/compat.py | 4 ++++ pycco/main.py | 44 +++++++++++++++++++++++++++----------------- tests/test_pycco.py | 29 ++++++++++++++++++----------- 4 files changed, 51 insertions(+), 28 deletions(-) create mode 100644 pycco/compat.py diff --git a/.travis.yml b/.travis.yml index bfbe563..62e7c6b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,12 @@ language: python python: - '2.7' + - '3.5' install: - 'pip install -r requirements.txt' - 'pip install -r requirements.test.txt' script: - 'py.test --cov=pycco tests/' + - 'python -m pycco.main pycco/main.py' after_success: - coveralls diff --git a/pycco/compat.py b/pycco/compat.py new file mode 100644 index 0000000..6660531 --- /dev/null +++ b/pycco/compat.py @@ -0,0 +1,4 @@ +try: + pycco_unichr = unichr +except NameError: + pycco_unichr = chr diff --git a/pycco/main.py b/pycco/main.py index df2b2bc..cde05d7 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +from __future__ import print_function """ "**Pycco**" is a Python port of [Docco](http://jashkenas.github.com/docco/): @@ -35,7 +36,7 @@ Or, to install the latest source def generate_documentation(source, outdir=None, preserve_paths=True, - language=None): + language=None, encoding="utf8"): """ Generate the documentation for a source file by reading it in, splitting it up into comment/code sections, highlighting them for the appropriate @@ -44,7 +45,7 @@ def generate_documentation(source, outdir=None, preserve_paths=True, if not outdir: raise TypeError("Missing the required 'outdir' keyword argument.") - code = open(source, "r").read() + code = open(source, "rb").read().decode(encoding) return _generate_documentation(source, code, outdir, preserve_paths, language) @@ -226,6 +227,8 @@ def highlight(sections, language, preserve_paths=True, outdir=None): docs_text = unicode(section["docs_text"]) except UnicodeError: docs_text = unicode(section["docs_text"].decode('utf-8')) + except NameError: + docs_text = section['docs_text'] section["docs_html"] = markdown(preprocess(docs_text, preserve_paths=preserve_paths, outdir=outdir)) @@ -361,9 +364,9 @@ def get_language(source, code, language=None): else: raise ValueError() except ValueError: - # If pygments can't find any lexers, it will raise its own - # subclass of ValueError. We will catch it and raise ours - # for consistency. + # If pygments can't find any lexers, it will raise its own + # subclass of ValueError. We will catch it and raise ours + # for consistency. raise ValueError("Can't figure out the language!") @@ -403,15 +406,20 @@ def shift(list, default): return default +def remove_control_chars(s): + # Sanitization regexp copied from + # http://stackoverflow.com/questions/92438/stripping-non-printable-characters-from-a-string-in-python + from pycco.compat import pycco_unichr + control_chars = ''.join(map(pycco_unichr, list(range(0, 32)) + list(range(127, 160)))) + control_char_re = re.compile(u'[{}]'.format(re.escape(control_chars))) + return control_char_re.sub('', s) + + def ensure_directory(directory): """ Sanitize directory string and ensure that the destination directory exists. """ - # Sanitization regexp copied from - # http://stackoverflow.com/questions/92438/stripping-non-printable-characters-from-a-string-in-python - control_chars = ''.join(map(unichr, range(0, 32) + range(127, 160))) - control_char_re = re.compile(u'[{}]'.format(re.escape(control_chars))) - directory = control_char_re.sub('', directory) + directory = remove_control_chars(directory) if not os.path.isdir(directory): os.makedirs(directory) @@ -434,7 +442,7 @@ highlight_start = "
    "
     highlight_end = "
    " -def process(sources, preserve_paths=True, outdir=None, language=None): +def process(sources, preserve_paths=True, outdir=None, language=None, encoding="utf8"): """For each source file passed as argument, generate the documentation.""" if not outdir: @@ -447,8 +455,8 @@ def process(sources, preserve_paths=True, outdir=None, language=None): # Proceed to generating the documentation. if sources: outdir = ensure_directory(outdir) - css = open(path.join(outdir, "pycco.css"), "w") - css.write(pycco_styles) + css = open(path.join(outdir, "pycco.css"), "wb") + css.write(pycco_styles.encode(encoding)) css.close() def next_file(): @@ -460,11 +468,13 @@ def process(sources, preserve_paths=True, outdir=None, language=None): except OSError: pass - with open(dest, "w") as f: - f.write(generate_documentation(s, preserve_paths=preserve_paths, outdir=outdir, - language=language)) + with open(dest, "wb") as f: + f.write(generate_documentation(s, preserve_paths=preserve_paths, + outdir=outdir, + language=language, + encoding=encoding)) - print "pycco = {} -> {}".format(s, dest) + print("pycco = {} -> {}".format(s, dest)) if sources: next_file() diff --git a/tests/test_pycco.py b/tests/test_pycco.py index 04cb57e..22503c2 100644 --- a/tests/test_pycco.py +++ b/tests/test_pycco.py @@ -1,18 +1,24 @@ import copy +import os import tempfile +import time + import pytest -import os -import re from hypothesis import given, example, assume from hypothesis.strategies import lists, text, booleans, choices, none import pycco.main as p + PYTHON = p.languages['.py'] PYCCO_SOURCE = 'pycco/main.py' FOO_FUNCTION = """def foo():\n return True""" +def get_language(choice): + return choice(list(p.languages.values())) + + @given(lists(text()), text()) def test_shift(fragments, default): if fragments == []: @@ -33,7 +39,7 @@ def test_destination(filepath, preserve_paths, outdir): @given(choices(), text()) def test_parse(choice, source): - l = choice(p.languages.values()) + l = get_language(choice) parsed = p.parse(source, l) assert [{"code_text", "docs_text"} == set(s.keys()) for s in parsed] @@ -69,7 +75,11 @@ def test_get_language_bad_source(source): with pytest.raises(ValueError) as e: assert p.get_language(source, "badlang") - assert e.value.message == "Can't figure out the language!" + msg = "Can't figure out the language!" + try: + assert e.value.message == msg + except AttributeError: + assert e.value.args[0] == msg @given(text() | none()) @@ -80,16 +90,13 @@ def test_get_language_bad_code(code): @given(text(max_size=64)) def test_ensure_directory(dir_name): - tempdir = os.path.join(tempfile.gettempdir(), dir_name) + tempdir = os.path.join(tempfile.gettempdir(), str(int(time.time())), dir_name) - # Copy and paste sanitization from function, but only for housekeeping. We + # Use sanitization from function, but only for housekeeping. We # pass in the unsanitized string to the function. - control_chars = ''.join(map(unichr, range(0, 32) + range(127, 160))) - control_char_re = re.compile(u'[{}]'.format(re.escape(control_chars))) - safe_name = control_char_re.sub('', tempdir) + safe_name = p.remove_control_chars(dir_name) - if not os.path.isdir(safe_name): - assume(os.access(safe_name, os.W_OK)) + if not os.path.isdir(safe_name) and os.access(safe_name, os.W_OK): p.ensure_directory(tempdir) assert os.path.isdir(safe_name) -- cgit v1.2.1 From ff586cd08ad4e226ea5d65eda8683afb5a5c9373 Mon Sep 17 00:00:00 2001 From: Zach Smith Date: Sat, 7 Nov 2015 19:39:42 -0500 Subject: Unwrap comprehension in test --- tests/test_pycco.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_pycco.py b/tests/test_pycco.py index 22503c2..5a38dd5 100644 --- a/tests/test_pycco.py +++ b/tests/test_pycco.py @@ -41,7 +41,8 @@ def test_destination(filepath, preserve_paths, outdir): def test_parse(choice, source): l = get_language(choice) parsed = p.parse(source, l) - assert [{"code_text", "docs_text"} == set(s.keys()) for s in parsed] + for s in parsed: + assert {"code_text", "docs_text"} == set(s.keys()) def test_skip_coding_directive(): -- cgit v1.2.1 From 4d95d001345011e1e0efadb54dbb19b616193b1a Mon Sep 17 00:00:00 2001 From: Jonathan Sharpe Date: Sun, 8 Nov 2015 16:28:09 +0000 Subject: Resynced whitespace --- pycco/main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pycco/main.py b/pycco/main.py index 6d45673..eeaf816 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -172,7 +172,6 @@ def preprocess(comment, preserve_paths=True, outdir=None): # Check if the match contains an anchor if '#' in match.group(1): name, anchor = match.group(1).split('#') - return " [{}]({}#{})".format(name, path.basename(destination(name, preserve_paths=preserve_paths, -- cgit v1.2.1 From b82a8e45fa5b17ef5f15a410bfd5b694e5a07cba Mon Sep 17 00:00:00 2001 From: Jonathan Sharpe Date: Sun, 8 Nov 2015 16:46:23 +0000 Subject: Added test for parsing of comments with only cross-references --- tests/test_pycco.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_pycco.py b/tests/test_pycco.py index 5a38dd5..7248a1a 100644 --- a/tests/test_pycco.py +++ b/tests/test_pycco.py @@ -60,6 +60,13 @@ def test_multi_line_leading_spaces(): assert parsed[0]["docs_text"] == "This is a\ncomment that\nis indented\n" +def test_comment_with_only_cross_ref(): + source = '''# ==Link Target==\n\ndef test_link():\n """[[testing.py#link-target]]"""\n pass''' + sections = p.parse(source, PYTHON) + p.highlight(sections, PYTHON, outdir=tempfile.gettempdir()) + assert sections[1]['docs_html'] == '

    testing.py

    ' + + @given(text(), text()) def test_get_language_specify_language(source, code): assert p.get_language(source, code, language="python") == p.languages['.py'] -- cgit v1.2.1 From 2ac3d174fbd0733653276baec324d239c81409dd Mon Sep 17 00:00:00 2001 From: Andrew Trask Date: Sat, 14 Nov 2015 13:29:58 -0600 Subject: fixed pyco test --- README | 25 ------ README.md | 30 +++++++ pycco/compat.py | 4 + pycco/main.py | 226 ++++++++++++++++++++++++++++++-------------------- requirements.test.txt | 3 + requirements.txt | 3 + tests/__init__.py | 0 tests/test_pycco.py | 141 +++++++++++++++++++++++++++++++ 8 files changed, 318 insertions(+), 114 deletions(-) delete mode 100644 README create mode 100644 README.md create mode 100644 pycco/compat.py create mode 100644 requirements.test.txt create mode 100644 requirements.txt create mode 100644 tests/__init__.py create mode 100644 tests/test_pycco.py diff --git a/README b/README deleted file mode 100644 index 5343728..0000000 --- a/README +++ /dev/null @@ -1,25 +0,0 @@ -888888b. -888 Y88b -888 888 -888 d88P 888 888 .d8888b .d8888b .d88b. -8888888P" 888 888 d88P" d88P" d88""88b -888 888 888 888 888 888 888 -888 Y88b 888 Y88b. Y88b. Y88..88P -888 "Y88888 "Y8888P "Y8888P "Y88P" - 888 - Y8b d88P - "Y88P" - -Pycco is a Python port of Docco: the original quick-and-dirty, hundred-line- -long, literate-programming-style documentation generator. For more information, -see: - -http://fitzgen.github.com/pycco/ - -Others: - -CoffeeScript (Original) - http://jashkenas.github.com/docco/ - -Ruby - http://rtomayko.github.com/rocco/ - -Sh - http://rtomayko.github.com/shocco/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..9cad492 --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +``` +888888b. +888 Y88b +888 888 +888 d88P 888 888 .d8888b .d8888b .d88b. +8888888P" 888 888 d88P" d88P" d88""88b +888 888 888 888 888 888 888 +888 Y88b 888 Y88b. Y88b. Y88..88P +888 "Y88888 "Y8888P "Y8888P "Y88P" + 888 + Y8b d88P + "Y88P" +``` + +Pycco is a Python port of Docco: the original quick-and-dirty, hundred-line- +long, literate-programming-style documentation generator. For more information, +see: + +http://fitzgen.github.com/pycco/ + +Others: + +CoffeeScript (Original) - http://jashkenas.github.com/docco/ + +Ruby - http://rtomayko.github.com/rocco/ + +Sh - http://rtomayko.github.com/shocco/ + +[![Build Status](https://travis-ci.org/subsetpark/pycco.svg?branch=hypothesis)](https://travis-ci.org/subsetpark/pycco) +[![Coverage Status](https://coveralls.io/repos/subsetpark/pycco/badge.svg?branch=hypothesis&service=github)](https://coveralls.io/github/subsetpark/pycco?branch=hypothesis) diff --git a/pycco/compat.py b/pycco/compat.py new file mode 100644 index 0000000..6660531 --- /dev/null +++ b/pycco/compat.py @@ -0,0 +1,4 @@ +try: + pycco_unichr = unichr +except NameError: + pycco_unichr = chr diff --git a/pycco/main.py b/pycco/main.py index 6fd982f..676f6ff 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +from __future__ import print_function """ "**Pycco**" is a Python port of [Docco](http://jashkenas.github.com/docco/): @@ -33,8 +34,9 @@ Or, to install the latest source # === Main Documentation Generation Functions === + def generate_documentation(source, outdir=None, preserve_paths=True, - language=None): + language=None, encoding="utf8"): """ Generate the documentation for a source file by reading it in, splitting it up into comment/code sections, highlighting them for the appropriate @@ -43,18 +45,25 @@ def generate_documentation(source, outdir=None, preserve_paths=True, if not outdir: raise TypeError("Missing the required 'outdir' keyword argument.") - code = open(source, "r").read() - language = get_language(source, code, language=language) - sections = parse(source, code, language) - highlight(source, sections, language, preserve_paths=preserve_paths, outdir=outdir) - return generate_html(source, sections, preserve_paths=preserve_paths, outdir=outdir) + code = open(source, "rb").read().decode(encoding) + return _generate_documentation(source, code, outdir, preserve_paths, language) + -def parse(source, code, language): +def _generate_documentation(file_path, code, outdir, preserve_paths, language): + """ + Helper function to allow documentation generation without file handling. + """ + language = get_language(file_path, code, language=language) + sections = parse(code, language) + highlight(sections, language, preserve_paths=preserve_paths, outdir=outdir) + return generate_html(file_path, sections, preserve_paths=preserve_paths, outdir=outdir) + + +def parse(code, language): """ Given a string of source code, parse out each comment and the code that follows it, and create an individual **section** for it. Sections take the form: - { "docs_text": ..., "docs_html": ..., "code_text": ..., @@ -76,7 +85,6 @@ def parse(source, code, language): lines.pop(linenum) break - def save(docs, code): if docs or code: sections.append({ @@ -87,27 +95,25 @@ def parse(source, code, language): # Setup the variables to get ready to check for multiline comments multi_line = False multi_string = False - multi_line_delimiters = [language.get("multistart"), language.get("multiend")] + multistart, multiend = language.get("multistart"), language.get("multiend") + comment_matcher = language['comment_matcher'] for line in lines: - process_as_code = False # Only go into multiline comments section when one of the delimiters is # found to be at the start of a line - if all(multi_line_delimiters) and any([line.lstrip().startswith(delim) or line.rstrip().endswith(delim) for delim in multi_line_delimiters]): - if not multi_line: - multi_line = True - - else: - multi_line = False + if multistart and multiend and \ + any(line.lstrip().startswith(delim) or line.rstrip().endswith(delim) + for delim in (multistart, multiend)): + multi_line = not multi_line if (multi_line - and line.strip().endswith(language.get("multiend")) - and len(line.strip()) > len(language.get("multiend"))): + and line.strip().endswith(multiend) + and len(line.strip()) > len(multiend)): multi_line = False - if((not line.strip().startswith(language.get("multistart")) and not multi_line) or multi_string): + if((not line.strip().startswith(multistart) and not multi_line) or multi_string): process_as_code = True @@ -119,8 +125,8 @@ def parse(source, code, language): else: # Get rid of the delimiters so that they aren't in the final docs - line = line.replace(language["multistart"], '') - line = line.replace(language["multiend"], '') + line = line.replace(multistart, '') + line = line.replace(multiend, '') docs_text += line.strip() + '\n' indent_level = re.match("\s*", line).group(0) @@ -131,22 +137,23 @@ def parse(source, code, language): elif multi_line: # Remove leading spaces - if re.match(r' {%d}' % len(indent_level), line): + if re.match(r' {{{:d}}}'.format(len(indent_level)), line): docs_text += line[len(indent_level):] + '\n' else: docs_text += line + '\n' - elif re.match(language["comment_matcher"], line): + elif re.match(comment_matcher, line): if has_code: save(docs_text, code_text) has_code = docs_text = code_text = '' - docs_text += re.sub(language["comment_matcher"], "", line) + "\n" + docs_text += re.sub(comment_matcher, "", line) + "\n" else: process_as_code = True if(process_as_code): - if code_text and any([line.lstrip().startswith(x) for x in ['class ', 'def ', '@']]): + if code_text and any(line.lstrip().startswith(x) + for x in ['class ', 'def ', '@']): if not code_text.lstrip().startswith("@"): save(docs_text, code_text) code_text = has_code = docs_text = '' @@ -154,14 +161,14 @@ def parse(source, code, language): has_code = True code_text += line + '\n' - save(docs_text, code_text) return sections # === Preprocessing the comments === -def preprocess(comment, section_nr, preserve_paths=True, outdir=None): + +def preprocess(comment, preserve_paths=True, outdir=None): """ Add cross-references before having the text processed by markdown. It's possible to reference another file, like this : `[[main.py]]` which renders @@ -174,6 +181,7 @@ def preprocess(comment, section_nr, preserve_paths=True, outdir=None): if not outdir: raise TypeError("Missing the required 'outdir' keyword argument.") + def sanitize_section_name(name): return "-".join(name.lower().strip().split(" ")) @@ -181,33 +189,37 @@ def preprocess(comment, section_nr, preserve_paths=True, outdir=None): # Check if the match contains an anchor if '#' in match.group(1): name, anchor = match.group(1).split('#') - return " [%s](%s#%s)" % (name, - path.basename(destination(name, - preserve_paths=preserve_paths, - outdir=outdir)), - anchor) + return " [{}]({}#{})".format(name, + path.basename(destination(name, + preserve_paths=preserve_paths, + outdir=outdir)), + anchor) else: - return " [%s](%s)" % (match.group(1), - path.basename(destination(match.group(1), - preserve_paths=preserve_paths, - outdir=outdir))) + return " [{}]({})".format(match.group(1), + path.basename(destination(match.group(1), + preserve_paths=preserve_paths, + outdir=outdir))) def replace_section_name(match): - return '%(lvl)s %(name)s' % { - "lvl" : re.sub('=', '#', match.group(1)), - "id" : sanitize_section_name(match.group(2)), - "name" : match.group(2) - } + """ + Replace equals-sign-formatted section names with anchor links. + """ + return '{lvl} {name}'.format( + lvl=re.sub('=', '#', match.group(1)), + id=sanitize_section_name(match.group(2)), + name=match.group(2) + ) comment = re.sub('^([=]+)([^=]+)[=]*\s*$', replace_section_name, comment) - comment = re.sub('[^`]\[\[(.+?)\]\]', replace_crossref, comment) + comment = re.sub('(?
    "
     # The end of each Pygments highlight block.
     highlight_end = "
    " -def process(sources, preserve_paths=True, outdir=None, language=None): + +def process(sources, preserve_paths=True, outdir=None, language=None, encoding="utf8"): """For each source file passed as argument, generate the documentation.""" if not outdir: @@ -426,9 +471,9 @@ def process(sources, preserve_paths=True, outdir=None, language=None): # Proceed to generating the documentation. if sources: - ensure_directory(outdir) - css = open(path.join(outdir, "pycco.css"), "w") - css.write(pycco_styles) + outdir = ensure_directory(outdir) + css = open(path.join(outdir, "pycco.css"), "wb") + css.write(pycco_styles.encode(encoding)) css.close() def next_file(): @@ -440,11 +485,13 @@ def process(sources, preserve_paths=True, outdir=None, language=None): except OSError: pass - with open(dest, "w") as f: - f.write(generate_documentation(s, preserve_paths=preserve_paths, outdir=outdir, - language=language)) + with open(dest, "wb") as f: + f.write(generate_documentation(s, preserve_paths=preserve_paths, + outdir=outdir, + language=language, + encoding=encoding)) - print "pycco = %s -> %s" % (s, dest) + print("pycco = {} -> {}".format(s, dest)) if sources: next_file() @@ -468,6 +515,7 @@ def monitor(sources, opts): class RegenerateHandler(watchdog.events.FileSystemEventHandler): """A handler for recompiling files which triggered watchdog events""" + def on_modified(self, event): """Regenerate documentation for a file which triggered an event""" # Re-generate documentation from a source file if it was listed on diff --git a/requirements.test.txt b/requirements.test.txt new file mode 100644 index 0000000..8439fc2 --- /dev/null +++ b/requirements.test.txt @@ -0,0 +1,3 @@ +hypothesis==1.14.0 +pytest-cov==2.2.0 +coveralls==1.1 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..38964da --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +pystache==0.5.4 +Pygments==2.0.2 +markdown==2.6.3 diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_pycco.py b/tests/test_pycco.py new file mode 100644 index 0000000..6159cfd --- /dev/null +++ b/tests/test_pycco.py @@ -0,0 +1,141 @@ +import copy +import os +import tempfile +import time + +import pytest +from hypothesis import given, example, assume +from hypothesis.strategies import lists, text, booleans, choices, none + +import pycco.main as p + + +PYTHON = p.languages['.py'] +PYCCO_SOURCE = 'pycco/main.py' +FOO_FUNCTION = """def foo():\n return True""" + + +def get_language(choice): + return choice(list(p.languages.values())) + + +@given(lists(text()), text()) +def test_shift(fragments, default): + if fragments == []: + assert p.shift(fragments, default) == default + else: + fragments2 = copy.copy(fragments) + head = p.shift(fragments, default) + assert [head] + fragments == fragments2 + + +@given(text(), booleans(), text(min_size=1)) +@example("/foo", True, "0") +def test_destination(filepath, preserve_paths, outdir): + dest = p.destination(filepath, preserve_paths=preserve_paths, outdir=outdir) + assert dest.startswith(outdir) + assert dest.endswith(".html") + + +@given(choices(), text()) +def test_parse(choice, source): + l = get_language(choice) + parsed = p.parse(source, l) + for s in parsed: + assert {"code_text", "docs_text"} == set(s.keys()) + + +def test_skip_coding_directive(): + source = "# -*- coding: utf-8 -*-\n" + FOO_FUNCTION + parsed = p.parse(source, PYTHON) + for section in parsed: + assert "coding" not in section['code_text'] + + +def test_multi_line_leading_spaces(): + source = "# This is a\n# comment that\n# is indented\n" + source += FOO_FUNCTION + parsed = p.parse(source, PYTHON) + # The resulting comment has leading spaces stripped out. + assert parsed[0]["docs_text"] == "This is a\ncomment that\nis indented\n" + + +def test_comment_with_only_cross_ref(): + source = '''# ==Link Target==\n\ndef test_link():\n """[[testing.py#link-target]]"""\n pass''' + sections = p.parse(source, PYTHON) + p.highlight(sections, PYTHON, outdir=tempfile.gettempdir()) + assert sections[1]['docs_html'] == '

    testing.py

    ' + + +@given(text(), text()) +def test_get_language_specify_language(source, code): + assert p.get_language(source, code, language="python") == p.languages['.py'] + + with pytest.raises(ValueError): + p.get_language(source, code, language="non-existent") + + +@given(text() | none()) +def test_get_language_bad_source(source): + code = "#!/usr/bin/python\n" + code += FOO_FUNCTION + assert p.get_language(source, code) == PYTHON + with pytest.raises(ValueError) as e: + assert p.get_language(source, "badlang") + + msg = "Can't figure out the language!" + try: + assert e.value.message == msg + except AttributeError: + assert e.value.args[0] == msg + + +@given(text() | none()) +def test_get_language_bad_code(code): + source = "test.py" + assert p.get_language(source, code) == PYTHON + + +@given(text(max_size=64)) +def test_ensure_directory(dir_name): + tempdir = os.path.join(tempfile.gettempdir(), str(int(time.time())), dir_name) + + # Use sanitization from function, but only for housekeeping. We + # pass in the unsanitized string to the function. + safe_name = p.remove_control_chars(dir_name) + + if not os.path.isdir(safe_name) and os.access(safe_name, os.W_OK): + p.ensure_directory(tempdir) + assert os.path.isdir(safe_name) + +# The following functions get good test coverage, but effort should be put into +# decomposing the functions they test and actually testing their output. + + +def test_generate_documentation(): + p.generate_documentation(PYCCO_SOURCE, outdir=tempfile.gettempdir()) + + +@given(booleans(), choices()) +def test_process(preserve_paths, choice): + lang_name = choice([l["name"] for l in p.languages.values()]) + p.process([PYCCO_SOURCE], preserve_paths=preserve_paths, outdir=tempfile.gettempdir(), language=lang_name) + + +def test_ensure_multiline_string_support(): + code = '''x = """ +how about this? +""" + +y = z # is this where it should be? + +# how did this get so *BIG!* + +def x(): + """how would you fix? + """''' + + docs_code_tuple_list = p.parse(code,PYTHON) + + assert docs_code_tuple_list[0]['docs_text'] == '' + assert "#" not in docs_code_tuple_list[1]['docs_text'] -- cgit v1.2.1 From 1071b68d726d4b8e75bf342ba3a07815e2d4d735 Mon Sep 17 00:00:00 2001 From: Andrew Trask Date: Sat, 14 Nov 2015 13:39:01 -0600 Subject: merge --- pycco/main.py | 14 -------------- tests/test_pycco.py | 4 +--- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index bfce2ea..8c70cc9 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -48,19 +48,6 @@ def generate_documentation(source, outdir=None, preserve_paths=True, code = open(source, "rb").read().decode(encoding) return _generate_documentation(source, code, outdir, preserve_paths, language) -<<<<<<< HEAD - -def _generate_documentation(file_path, code, outdir, preserve_paths, language): - """ - Helper function to allow documentation generation without file handling. - """ - language = get_language(file_path, code, language=language) - sections = parse(code, language) - highlight(sections, language, preserve_paths=preserve_paths, outdir=outdir) - return generate_html(file_path, sections, preserve_paths=preserve_paths, outdir=outdir) - -======= - def _generate_documentation(file_path, code, outdir, preserve_paths, language): """ Helper function to allow documentation generation without file handling. @@ -70,7 +57,6 @@ def _generate_documentation(file_path, code, outdir, preserve_paths, language): highlight(sections, language, preserve_paths=preserve_paths, outdir=outdir) return generate_html(file_path, sections, preserve_paths=preserve_paths, outdir=outdir) ->>>>>>> bbeee06f1222fa47439717d2e4b642a41e9d7f4b def parse(code, language): """ diff --git a/tests/test_pycco.py b/tests/test_pycco.py index 2cc2b15..d0cf6a3 100644 --- a/tests/test_pycco.py +++ b/tests/test_pycco.py @@ -120,7 +120,6 @@ def test_generate_documentation(): def test_process(preserve_paths, choice): lang_name = choice([l["name"] for l in p.languages.values()]) p.process([PYCCO_SOURCE], preserve_paths=preserve_paths, outdir=tempfile.gettempdir(), language=lang_name) -<<<<<<< HEAD def test_ensure_multiline_string_support(): @@ -140,5 +139,4 @@ def x(): assert docs_code_tuple_list[0]['docs_text'] == '' assert "#" not in docs_code_tuple_list[1]['docs_text'] -======= ->>>>>>> bbeee06f1222fa47439717d2e4b642a41e9d7f4b + -- cgit v1.2.1 From 17391b9c0f1a6b1ed5f062ba85a23a3511155678 Mon Sep 17 00:00:00 2001 From: Andrew Trask Date: Sat, 14 Nov 2015 13:44:10 -0600 Subject: cleanup whitespace alignment --- pycco/main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pycco/main.py b/pycco/main.py index 8c70cc9..e99d4bb 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -48,6 +48,7 @@ def generate_documentation(source, outdir=None, preserve_paths=True, code = open(source, "rb").read().decode(encoding) return _generate_documentation(source, code, outdir, preserve_paths, language) + def _generate_documentation(file_path, code, outdir, preserve_paths, language): """ Helper function to allow documentation generation without file handling. -- cgit v1.2.1 From a3aadea52010e2c3e328e58b4d728a0f9c100113 Mon Sep 17 00:00:00 2001 From: Zach Smith Date: Thu, 19 Nov 2015 20:05:29 -0500 Subject: Formatting cleanup after merge. --- pycco/main.py | 26 +++++++++++++------------- tests/test_pycco.py | 15 +++++++-------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index e99d4bb..c9a3612 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -100,32 +100,32 @@ def parse(code, language): for line in lines: process_as_code = False - # Only go into multiline comments section when one of the delimiters is # found to be at the start of a line - if multistart and multiend and \ - any(line.lstrip().startswith(delim) or line.rstrip().endswith(delim) - for delim in (multistart, multiend)): + if multistart and multiend \ + and any(line.lstrip().startswith(delim) or line.rstrip().endswith(delim) + for delim in (multistart, multiend)): multi_line = not multi_line - if (multi_line - and line.strip().endswith(multiend) - and len(line.strip()) > len(multiend)): + if multi_line \ + and line.strip().endswith(multiend) \ + and len(line.strip()) > len(multiend): multi_line = False - - if((not line.strip().startswith(multistart) and not multi_line) or multi_string): + if not line.strip().startswith(multistart) and not multi_line \ + or multi_string: process_as_code = True - if(multi_string): + if multi_string: multi_line = False multi_string = False else: multi_string = True else: - # Get rid of the delimiters so that they aren't in the final docs + # Get rid of the delimiters so that they aren't in the final + # docs line = line.replace(multistart, '') line = line.replace(multiend, '') docs_text += line.strip() + '\n' @@ -152,8 +152,7 @@ def parse(code, language): else: process_as_code = True - if(process_as_code): - + if process_as_code: if code_text and any(line.lstrip().startswith(x) for x in ['class ', 'def ', '@']): if not code_text.lstrip().startswith("@"): @@ -516,6 +515,7 @@ def monitor(sources, opts): for source in sources) class RegenerateHandler(watchdog.events.FileSystemEventHandler): + """A handler for recompiling files which triggered watchdog events""" def on_modified(self, event): diff --git a/tests/test_pycco.py b/tests/test_pycco.py index d0cf6a3..8136c03 100644 --- a/tests/test_pycco.py +++ b/tests/test_pycco.py @@ -64,7 +64,7 @@ def test_comment_with_only_cross_ref(): source = '''# ==Link Target==\n\ndef test_link():\n """[[testing.py#link-target]]"""\n pass''' sections = p.parse(source, PYTHON) p.highlight(sections, PYTHON, outdir=tempfile.gettempdir()) - assert sections[1]['docs_html'] == '

    testing.py

    ' + assert sections[1]['docs_html'] == '

    testing.py

    ' @given(text(), text()) @@ -124,19 +124,18 @@ def test_process(preserve_paths, choice): def test_ensure_multiline_string_support(): code = '''x = """ -how about this? +multi-line-string """ -y = z # is this where it should be? +y = z # comment -# how did this get so *BIG!* +# *comment with formatting* def x(): - """how would you fix? + """multi-line-string """''' - docs_code_tuple_list = p.parse(code,PYTHON) + docs_code_tuple_list = p.parse(code, PYTHON) assert docs_code_tuple_list[0]['docs_text'] == '' - assert "#" not in docs_code_tuple_list[1]['docs_text'] - + assert "#" not in docs_code_tuple_list[1]['docs_text'] -- cgit v1.2.1 From 239816deb0db2a3136b5442bca7422faf67d953d Mon Sep 17 00:00:00 2001 From: Zach Smith Date: Thu, 19 Nov 2015 20:10:11 -0500 Subject: Bump version number -> 0.3.1 --- setup.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/setup.py b/setup.py index 4e70456..28b20b1 100644 --- a/setup.py +++ b/setup.py @@ -1,20 +1,20 @@ from setuptools import setup, find_packages setup( - name = "Pycco", - version = "0.3.0", - description = """A Python port of Docco: the original quick-and-dirty, + name="Pycco", + version="0.3.1", + description="""A Python port of Docco: the original quick-and-dirty, hundred-line-long, literate-programming-style documentation generator. """, - author = "Nick Fitzgerald", - author_email = "fitzgen@gmail.com", - url = "http://fitzgen.github.com/pycco", - packages = find_packages(), - entry_points = { - 'console_scripts': [ - 'pycco = pycco.main:main', - ] - }, - install_requires = ['markdown', 'pygments', 'pystache', 'smartypants'], - extras_require = {'monitoring': 'watchdog'}, - ) + author="Nick Fitzgerald", + author_email="fitzgen@gmail.com", + url="http://fitzgen.github.com/pycco", + packages=find_packages(), + entry_points={ + 'console_scripts': [ + 'pycco = pycco.main:main', + ] + }, + install_requires=['markdown', 'pygments', 'pystache', 'smartypants'], + extras_require={'monitoring': 'watchdog'}, +) -- cgit v1.2.1 From 326457dda6da647565806f6363d9fcf17b52fe64 Mon Sep 17 00:00:00 2001 From: Zach Smith Date: Mon, 28 Dec 2015 12:06:36 -0500 Subject: (minor) Update hypothesis versions, insert needed newline in docs --- AUTHORS | 1 + pycco/main.py | 3 +++ requirements.test.txt | 2 +- tests/test_pycco.py | 36 +++++++++++++++++++++++------------- 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/AUTHORS b/AUTHORS index 442c482..b6faf28 100644 --- a/AUTHORS +++ b/AUTHORS @@ -5,5 +5,6 @@ Christopher Gateley Jack Miller Morgan Goose Nick Fitzgerald +Zach Smith goosemo khamidou diff --git a/pycco/main.py b/pycco/main.py index c9a3612..22f889a 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -64,6 +64,7 @@ def parse(code, language): Given a string of source code, parse out each comment and the code that follows it, and create an individual **section** for it. Sections take the form: + { "docs_text": ..., "docs_html": ..., "code_text": ..., @@ -252,6 +253,8 @@ def highlight(sections, language, preserve_paths=True, outdir=None): outdir=outdir)) section["num"] = i + return sections + # === HTML Code generation === diff --git a/requirements.test.txt b/requirements.test.txt index 8439fc2..5db5948 100644 --- a/requirements.test.txt +++ b/requirements.test.txt @@ -1,3 +1,3 @@ -hypothesis==1.14.0 +hypothesis==1.18.1 pytest-cov==2.2.0 coveralls==1.1 diff --git a/tests/test_pycco.py b/tests/test_pycco.py index 8136c03..be4e620 100644 --- a/tests/test_pycco.py +++ b/tests/test_pycco.py @@ -108,19 +108,6 @@ def test_ensure_directory(dir_name): p.ensure_directory(tempdir) assert os.path.isdir(safe_name) -# The following functions get good test coverage, but effort should be put into -# decomposing the functions they test and actually testing their output. - - -def test_generate_documentation(): - p.generate_documentation(PYCCO_SOURCE, outdir=tempfile.gettempdir()) - - -@given(booleans(), choices()) -def test_process(preserve_paths, choice): - lang_name = choice([l["name"] for l in p.languages.values()]) - p.process([PYCCO_SOURCE], preserve_paths=preserve_paths, outdir=tempfile.gettempdir(), language=lang_name) - def test_ensure_multiline_string_support(): code = '''x = """ @@ -139,3 +126,26 @@ def x(): assert docs_code_tuple_list[0]['docs_text'] == '' assert "#" not in docs_code_tuple_list[1]['docs_text'] + + +def test_indented_block(): + + code = '''""" +To install Pycco, simply + + pip install pycco +""" +''' + parsed = p.parse(code, PYTHON) + highlighted = p.highlight(parsed, PYTHON, outdir=tempfile.gettempdir()) + print highlighted + + +def test_generate_documentation(): + p.generate_documentation(PYCCO_SOURCE, outdir=tempfile.gettempdir()) + + +@given(booleans(), choices()) +def test_process(preserve_paths, choice): + lang_name = choice([l["name"] for l in p.languages.values()]) + p.process([PYCCO_SOURCE], preserve_paths=preserve_paths, outdir=tempfile.gettempdir(), language=lang_name) -- cgit v1.2.1 From 6502a836493c2dc19fcd1521225ba4c794f7d82c Mon Sep 17 00:00:00 2001 From: Zach Smith Date: Mon, 28 Dec 2015 12:26:30 -0500 Subject: Change references to new home at pycco-docs --- pycco/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pycco/main.py b/pycco/main.py index 22f889a..aaca009 100644 --- a/pycco/main.py +++ b/pycco/main.py @@ -18,7 +18,7 @@ If you install Pycco, you can run it from the command-line: This will generate linked HTML documentation for the named source files, saving it into a `docs` folder by default. -The [source for Pycco](https://github.com/fitzgen/pycco) is available on GitHub, +The [source for Pycco](https://github.com/pycco-docs/pycco) is available on GitHub, and released under the MIT license. To install Pycco, simply @@ -27,7 +27,7 @@ To install Pycco, simply Or, to install the latest source - git clone git://github.com/fitzgen/pycco.git + git clone git://github.com/pycco-docs/pycco.git cd pycco python setup.py install """ -- cgit v1.2.1 From 725ecf8bc1022c3816dbea65ea62061cbaf6094d Mon Sep 17 00:00:00 2001 From: Zach Smith Date: Mon, 28 Dec 2015 12:32:28 -0500 Subject: Make indent test useful --- tests/test_pycco.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_pycco.py b/tests/test_pycco.py index be4e620..85eecc1 100644 --- a/tests/test_pycco.py +++ b/tests/test_pycco.py @@ -130,15 +130,16 @@ def x(): def test_indented_block(): - code = '''""" -To install Pycco, simply + code = '''"""To install Pycco, simply pip install pycco """ ''' parsed = p.parse(code, PYTHON) highlighted = p.highlight(parsed, PYTHON, outdir=tempfile.gettempdir()) - print highlighted + pre_block = highlighted[0]['docs_html'] + assert '
    ' in pre_block
    +    assert '
    ' in pre_block def test_generate_documentation(): -- cgit v1.2.1 From b55033371b55b7d9410eee01d036531da3e42784 Mon Sep 17 00:00:00 2001 From: Zach Smith Date: Mon, 28 Dec 2015 14:40:39 -0500 Subject: Unicode-proof index generation --- AUTHORS | 1 + pycco/generate_index.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index b6faf28..188d852 100644 --- a/AUTHORS +++ b/AUTHORS @@ -5,6 +5,7 @@ Christopher Gateley Jack Miller Morgan Goose Nick Fitzgerald +Steffen Kampmann Zach Smith goosemo khamidou diff --git a/pycco/generate_index.py b/pycco/generate_index.py index 2ddf301..9a56acb 100644 --- a/pycco/generate_index.py +++ b/pycco/generate_index.py @@ -50,9 +50,9 @@ def generate_tree_html(tree): items = [] for node, subtree in sorted(compat_items(tree)): if 'entry' in subtree: - html = '
  • {}
  • '.format(subtree['entry']['relpath'], node) + html = u'
  • {}
  • '.format(subtree['entry']['relpath'], node) else: - html = '
    {}
      {}
    '.format(node, generate_tree_html(subtree)) + html = u'
    {}
      {}
    '.format(node, generate_tree_html(subtree)) items.append(html) -- cgit v1.2.1 From 0d85d161977bcfd9bb26b930ca13e66fd0e3786f Mon Sep 17 00:00:00 2001 From: Zach Smith Date: Mon, 28 Dec 2015 14:53:12 -0500 Subject: (0.4) --- CONTRIBUTING.md | 9 ++------- LICENSE | 3 ++- README.md | 6 +++--- setup.py | 8 ++++---- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4351196..eafb7ed 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,12 +1,7 @@ # Contributing to Pycco -This project currently lacks an active maintainer. - -The original author (Nick Fitzgerald) no longer maintains this project actively -and the current maintainer (Trey Hunner) no longer uses Pycco actively. +[Zach Smith](http://zdsmith.com) is the current maintainer of this project. ## Help Us Out -If you would like to help merge pull requests and manage issues email -Trey Hunner and Nick Fitzgerald so you can be granted access to manage the -repository. +Feel free to contribute by opening a pull request on this project's [GitHub repo](https://github.com/pycco-docs/pycco). All requests with documented and tested code will be gladly reviewed. diff --git a/LICENSE b/LICENSE index fbb0a92..de4c3a7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,5 @@ Copyright (c) 2010 Nick Fitzgerald +Copyright (c) 2016 Zachary Smith Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -19,4 +20,4 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Parts of Pycco are taken from Docco, see http://github.com/jashkenas/docco for -more information. \ No newline at end of file +more information. diff --git a/README.md b/README.md index 9cad492..73bca0a 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Pycco is a Python port of Docco: the original quick-and-dirty, hundred-line- long, literate-programming-style documentation generator. For more information, see: -http://fitzgen.github.com/pycco/ +https://pycco-docs.github.io/pycco/ Others: @@ -26,5 +26,5 @@ Ruby - http://rtomayko.github.com/rocco/ Sh - http://rtomayko.github.com/shocco/ -[![Build Status](https://travis-ci.org/subsetpark/pycco.svg?branch=hypothesis)](https://travis-ci.org/subsetpark/pycco) -[![Coverage Status](https://coveralls.io/repos/subsetpark/pycco/badge.svg?branch=hypothesis&service=github)](https://coveralls.io/github/subsetpark/pycco?branch=hypothesis) +[![Build Status](https://travis-ci.org/pycco-docs/pycco.svg?branch=master)](https://travis-ci.org/pycco-docs/pycco) +[![Coverage Status](https://coveralls.io/repos/pycco-docs/pycco/badge.svg?branch=master&service=github)](https://coveralls.io/github/pycco-docs/pycco?branch=master) diff --git a/setup.py b/setup.py index 28b20b1..1ba701b 100644 --- a/setup.py +++ b/setup.py @@ -2,13 +2,13 @@ from setuptools import setup, find_packages setup( name="Pycco", - version="0.3.1", + version="0.4", description="""A Python port of Docco: the original quick-and-dirty, hundred-line-long, literate-programming-style documentation generator. """, - author="Nick Fitzgerald", - author_email="fitzgen@gmail.com", - url="http://fitzgen.github.com/pycco", + author="Zach Smith", + author_email="subsetpark@gmail.com", + url="https://pycco-docs.github.io/pycco/", packages=find_packages(), entry_points={ 'console_scripts': [ -- cgit v1.2.1 From 7e51c3babec9f5dac3487f42008c3da58f6da4fd Mon Sep 17 00:00:00 2001 From: Zach Smith Date: Mon, 28 Dec 2015 14:58:19 -0500 Subject: Set max path length in test --- tests/test_pycco.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_pycco.py b/tests/test_pycco.py index d008023..02ef008 100644 --- a/tests/test_pycco.py +++ b/tests/test_pycco.py @@ -156,7 +156,7 @@ def test_process(preserve_paths, index, choice): language=lang_name) -@given(lists(lists(text(min_size=1), min_size=1), min_size=1), lists(text(min_size=1), min_size=1)) +@given(lists(lists(text(min_size=1), min_size=1, max_size=30), min_size=1), lists(text(min_size=1), min_size=1)) def test_generate_index(path_lists, outdir_list): file_paths = [os.path.join(*path_list) for path_list in path_lists] outdir = os.path.join(*outdir_list) -- cgit v1.2.1 From 86296837d8ce61abed323dbb9cc14491519e7811 Mon Sep 17 00:00:00 2001 From: Zach Smith Date: Mon, 28 Dec 2015 15:16:27 -0500 Subject: Point correctly to stylesheet --- pycco/generate_index.py | 3 +-- setup.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pycco/generate_index.py b/pycco/generate_index.py index 9a56acb..ab0ead0 100644 --- a/pycco/generate_index.py +++ b/pycco/generate_index.py @@ -65,11 +65,10 @@ def generate_index(files, outdir): index of all files. """ tree = build_tree(files, outdir) - css_path = path.join(outdir, "pycco.css") rendered = pycco_template({ "title": 'Index', - "stylesheet": css_path, + "stylesheet": 'pycco.css', "sections": {'docs_html': generate_tree_html(tree)}, "source": '', }) diff --git a/setup.py b/setup.py index 1ba701b..6405ca9 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name="Pycco", - version="0.4", + version="0.4.1", description="""A Python port of Docco: the original quick-and-dirty, hundred-line-long, literate-programming-style documentation generator. """, -- cgit v1.2.1