Skip to content

Commit

Permalink
Write doc
Browse files Browse the repository at this point in the history
  • Loading branch information
pawamoy committed Dec 2, 2016
1 parent d2769ef commit 1a8e0e4
Show file tree
Hide file tree
Showing 13 changed files with 432 additions and 67 deletions.
8 changes: 5 additions & 3 deletions .landscape.yml
@@ -1,5 +1,6 @@
ignore-patterns:
- (^|/)\..+
- build/

strictness: veryhigh
autodetect: true
Expand Down Expand Up @@ -44,6 +45,7 @@ pep8:
# - PYR19
# - PYR16

# pep257:
# disable:
# - D000
pep257:
disable:
- D203

9 changes: 9 additions & 0 deletions setup.py
@@ -1,5 +1,13 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-

"""
Setup script.
Uses setuptools.
Long description is a concatenation of README.rst and CHANGELOG.rst.
"""

from __future__ import absolute_import, print_function

import io
Expand All @@ -11,6 +19,7 @@


def read(*names, **kwargs):
"""Read a file in current directory."""
return io.open(
join(dirname(__file__), *names),
encoding=kwargs.get('encoding', 'utf8')
Expand Down
11 changes: 11 additions & 0 deletions src/shellman/__init__.py
Expand Up @@ -6,4 +6,15 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

"""
Shellman package.
Shellman is a Python utility that read a file and search for special comments
beginning with two sharps (##). It can recognize doxygen-like tags, such as
brief, desc, fn, author, so you can write documentation in your shell scripts.
After having retrieved the documentation comments, shellman will be able to
write this documentation as text, man, or markdown format on stdout.
"""

__version__ = '0.1.0'
3 changes: 1 addition & 2 deletions src/shellman/__main__.py
Expand Up @@ -6,17 +6,16 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.


"""
Entrypoint module, in case you use `python -mshellman`.
Why does this file exist, and why __main__? For more info, read:
- https://www.python.org/dev/peps/pep-0338/
- https://docs.python.org/2/using/cmdline.html#cmdoption-m
- https://docs.python.org/3/using/cmdline.html#cmdoption-m
"""

import sys

from shellman.cli import main
Expand Down
12 changes: 8 additions & 4 deletions src/shellman/cli.py
Expand Up @@ -32,15 +32,19 @@

def main(argv=sys.argv):
"""
Main function.
Args:
argv (list): List of arguments
argv (list): expected path of file to read
Returns:
int: A return code
int: 0, unless exception
Does stuff.
Get the file to parse, construct a Doc object, get file's doc,
get the wanted format from environment variable SHELLMAN_FORMAT
(default to text), get the according formatter class, instantiate it
with acquired doc and write on stdout.
"""

f = argv[0]
if f == sys.argv[0]:
f = argv[1]
Expand Down
104 changes: 85 additions & 19 deletions src/shellman/doc.py
Expand Up @@ -6,33 +6,76 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

"""
Doc module.
This module contains the class Doc.
"""

import os
import re

from .tag import FN_TAG, FN_TAGS, TAGS, Tag


def _tag_value(line):
"""
Get the tag and/or its value from a documentation comment (line).
Args:
line (str): the documentation comment.
Returns:
tuple: tag, rest of the line. tag can be None.
"""
line = line.lstrip('#')
first_char = line.lstrip(' ')[0]
if first_char in '@\\':
words = line.lstrip(' ').split(' ')
return words[0][1:], ' '.join(words[1:])

if len(line) > 1:
if line[0] == ' ':
return None, line[1:]
return None, line


class Doc(object):
"""
Doc class.
Instantiate with the path of a file.
This class provides a public method ``read`` to use when you actually
want to read the file and get its documentation as dict of nested lists.
"""

def __init__(self, file):
"""
Init method.
Args:
file (str): path to the file to read.
"""
self.file = file
self.doc = {k: None for k in TAGS.keys()}
self.doc['_file'] = os.path.basename(self.file)
self.doc['_fn'] = []

@staticmethod
def tag_value(line):
line = line.lstrip('#')
first_char = line.lstrip(' ')[0]
if first_char in '@\\':
words = line.lstrip(' ').split(' ')
return words[0][1:], ' '.join(words[1:])
def _update_value(self, tag, value, end=False):
"""
Update the value of the given tag.
if len(line) > 1:
if line[0] == ' ':
return None, line[1:]
return None, line
It will append the value to the current tag or append a new tag and
initialize it with the value.
def update_value(self, tag, value, end=False):
Args:
tag (str): a doc tag such as brief, author, ...
value (str): the value written after the tag
end (bool): append a new tag (don't append value to current one)
Returns:
bool: True if tag has ended, False otherwise
"""
if TAGS[tag].occurrences == Tag.MANY:
if TAGS[tag].lines == Tag.MANY:
if self.doc[tag] is None:
Expand All @@ -53,7 +96,21 @@ def update_value(self, tag, value, end=False):
self.doc[tag] = value.rstrip('\n')
return False

def update_fn_value(self, tag, value, end=False):
def _update_fn_value(self, tag, value, end=False):
"""
Update the value of the given function tag.
It will append the value to the current tag or append a new tag and
initialize it with the value.
Args:
tag (str): a doc tag such as brief, author, ...
value (str): the value written after the tag
end (bool): append a new tag (don't append value to current one)
Returns:
bool: True if tag has ended, False otherwise
"""
if FN_TAGS[tag].occurrences == Tag.MANY:
if FN_TAGS[tag].lines == Tag.MANY:
if self.doc['_fn'][-1][tag] is None:
Expand All @@ -75,6 +132,15 @@ def update_fn_value(self, tag, value, end=False):
return False

def read(self):
"""
Read the file, build the documentation as dict of nested lists.
After a call to this method, documentation is still accessible via
self.doc attribute.
Returns:
dict: built documentation.
"""
current_tag = None
in_tag = False
in_function = False
Expand All @@ -85,32 +151,32 @@ def read(self):
current_tag = None
in_tag = False
elif re.search(r'^##', line):
tag, value = Doc.tag_value(line)
tag, value = _tag_value(line)
if tag is not None:
current_tag = tag
if tag == FN_TAG:
in_function = True
self.doc['_fn'].append(
{k: None for k in FN_TAGS.keys()})
in_tag = self.update_fn_value(
in_tag = self._update_fn_value(
current_tag, value, end=True)
else:
if in_function and tag in FN_TAGS.keys():
in_tag = self.update_fn_value(
in_tag = self._update_fn_value(
current_tag, value, end=True)
elif tag in TAGS.keys():
in_function = False
in_tag = self.update_value(
in_tag = self._update_value(
current_tag, value, end=True)
else:
continue # ignore invalid tags
else:
if in_tag:
if in_function:
in_tag = self.update_fn_value(
in_tag = self._update_fn_value(
current_tag, value)
else:
in_tag = self.update_value(current_tag, value)
in_tag = self._update_value(current_tag, value)
else:
pass # doc without tag, ignored
return self.doc
19 changes: 19 additions & 0 deletions src/shellman/formatter/__init__.py
Expand Up @@ -6,12 +6,31 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

"""
Formatter module.
This module contains one Python module for each formatter, plus one for
the formatter base class.
It also provides a convenient function to get a formatter class given a
string argument describing this format.
"""

from .man import ManFormatter
from .markdown import MarkdownFormatter
from .text import TextFormatter


def get_formatter(fmt):
"""
Formatter class getter, given a format.
Args:
fmt (str): format for which to get a formatter class
Returns:
a subclass of BaseFormatter class
"""
if fmt == 'text':
return TextFormatter
elif fmt == 'man':
Expand Down

0 comments on commit 1a8e0e4

Please sign in to comment.