Source code for line_diversion

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2018, Wolfgang Scherer, <Wolfgang.Scherer at gmx.de>
#
# This file is part of Documentation Standard.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
## |||:sec:||| Extract UML Diagrams                                   #a2
## start                                                              #a2
## floating note right                                                #a2
r"""line_diversion.py - extract UML diagram from source code

.. role:: rem(comment)

======  ====================
usage:  line_diversion.py [OPTIONS] [file ..]                         :rem:`a2`
or      import line_diversion
======  ====================

Options
=======
  ..                                                                  :rem:`a2`

  ===================== ==================================================
  -m, --match RX        only extract diagrams matching RX             :rem:`a2`
  -b, --file-base=BASE  Write output to files: `BASE` + `ID` + ".puml". :rem:`a2`
                        E.g. --file-base="file-base-" => file-base-a0.puml

  --show-bases          show base classes for class diagrams (default) :rem:`a2`
  --hide-bases          hide base classes for class diagrams          :rem:`a2`
  --ignore-bases CLASS  ignore base class for class diagrams          :rem:`a2`
                         (can be specified multiple times) (default: object, PyJsMo)

  -r, --replace  REP    register replacement REP. REP is a symbol and :rem:`a2`
                        a replacement separated by whitespace.        :rem:`a2`
  --verbatim            do not perform standard replacements          :rem:`a2`

  -q, --quiet           suppress warnings
  -v, --verbose         verbose test output
  -d, --debug[=NUM]     show debug information
  -h, --help            display this help message

  --template list       show available templates.
  --eide[=COMM]         Emacs IDE template list (implies --template list).
  --template[=NAME]     extract named template to standard
                        output. Default NAME is ``-``.
  --extract[=DIR]       extract adhoc files to directory DIR (default: ``.``)
  --explode[=DIR]       explode script with adhoc in directory DIR
                        (default ``__adhoc__``)
  --setup[=install]     explode script into temporary directory and call
                        `python setup.py install`
  --implode             implode script with adhoc

  -t, --test            run doc tests
  ===================== ==================================================

Module
======

These are the `UML diagrams`_ and type codes supported by `PlantUML`_
..                                                                    :rem:`a2`

============ ======================= ==========
| **Marker** | **Diagram**           |                                :rem:`a2`
============ ======================= ==========
| #c[0-9]+   | class diagram         |                                :rem:`a2`
| #o[0-9]+   | object diagram        |                                :rem:`a2`
| #p[0-9]+   | component diagram     |                                :rem:`a2`
| #d[0-9]+   | deployment diagram    |                                :rem:`a2`
| #u[0-9]+   | use case diagram      |                                :rem:`a2`
| #a[0-9]+   | activity diagram      |                                :rem:`a2`
| #s[0-9]+   | state machine diagram |                                :rem:`a2`
| #m[0-9]+   | sequence diagram      |                                :rem:`a2`
| #t[0-9]+   | timing diagram        |                                :rem:`a2`
============ ======================= ==========

+-------------------------+------+-------------------------------+------+
| - `Structure Diagrams`_ | Type | - `Behavior Diagrams`_        | Type |
+=========================+======+===============================+======+
| - `Class Diagram`_      |  c   | - `Use Case Diagram`_         |  u   |
+-------------------------+------+-------------------------------+------+
| - `Object Diagram`_     |  o   | - `Activity Diagram`_         |  a   |
+-------------------------+------+-------------------------------+------+
| - `Component Diagram`_  |  p   | - `State Machine Diagram`_    |  s   |
+-------------------------+------+-------------------------------+------+
| - `Deployment Diagram`_ |  d   | - `Message Sequence Diagram`_ |  m   |
+-------------------------+------+-------------------------------+------+
|                         |      | - `Timing Diagram`_           |  t   |
+-------------------------+------+-------------------------------+------+

This is the inheritance hierarchy of `UML diagrams`_ supported by
PlantUML (clickable in HTML):

.. uml:: _static/uml-diagram-hierarchy.puml

.. _`UML Diagrams`: https://www.uml-diagrams.org/uml-25-diagrams.html
.. _`PlantUML`: http://plantuml.com

Automatic Exports
=================

>>> for ex in __all__: printf(sformat('from {0} import {1}', __name__, ex))
from line_diversion import check_line_parse
from line_diversion import LineParser
from line_diversion import DiagramTiming
from line_diversion import DiagramMessageSequence
from line_diversion import DiagramInteraction
from line_diversion import DiagramStateMachine
from line_diversion import DiagramActivity
from line_diversion import DiagramUseCase
from line_diversion import DiagramBehavior
from line_diversion import DiagramDeployment
from line_diversion import DiagramComponent
from line_diversion import DiagramObject
from line_diversion import DiagramClass
from line_diversion import DiagramStructure
from line_diversion import Diagram
from line_diversion import LineDiversion

Explicit Exports
================

>>> if '__all_internal__' in globals():
...   for ex in __all_internal__:
...     printf(sformat('from {0} import {1}', __name__, ex))

.. _END_OF_HELP_line_diversion:

Details
=======

See :func:`check_line_parse` for comprehensive list of line matchers.

Prefix Match
------------

>>> printf(PREFIX_LP)
{
    "rx": [
        "^(\\s*)(?://+|/\\*+|;+|@:u?[bl]?comm_?@|--|#+|@[bl]?comm_?@|[.][.])([copduasmt][0-9]+)(?:\\s|$)",
        "0"
    ],
    "groups": {
        "whitespace": 1,
        "id": 2,
        "text": null,
        "keyword": null,
        "condition": null
    }
}

>>> mo, _id = PREFIX_LP.match('#''a3 if (test) then (yes)')
>>> printf(mo.groups())
('', 'a3')

Condition Match
---------------

>>> printf(COND_LP)
{
    "rx": [
        "^(\\s*)(break|class|def|done|elif|else|elseif|elsif|fi|for|if|while|\\})(?::?|\\s)\\s*(.*)\\s*(?://+|/\\*+|;+|@:u?[bl]?comm_?@|--|#+|@[bl]?comm_?@|[.][.])([copduasmt][0-9]+)(?:\\s(.*)|\\s*)$",
        "0"
    ],
    "groups": {
        "whitespace": 1,
        "id": 4,
        "text": 5,
        "keyword": 2,
        "condition": 3
    }
}

>>> mo, _id = COND_LP.match('        if remove_count:  #''a1 then (yeah)')
>>> printf(mo.groups())
('        ', 'if', 'remove_count:  ', 'a1', 'then (yeah)')

>>> mo, _id = COND_LP.match('} #''a1')
>>> printf(mo.groups())
('', '}', '', 'a1', None)

Action Match
------------

>>> printf(ACT_LP)
{
    "rx": [
        "^(\\s*)(?:(?://+|/\\*+|;+|@:u?[bl]?comm_?@|--|#+|@[bl]?comm_?@|[.][.]) )?(.*)\\s*(?://+|/\\*+|;+|@:u?[bl]?comm_?@|--|#+|@[bl]?comm_?@|[.][.])([copduasmt][0-9]+) (:[];|<>/}]?|[];|<>/}.-])\\s*(?:(#[0-9A-Za-z]+|backwards)(?:\\s+|$))?(?:\\s(.*)|\\s*)$",
        "0"
    ],
    "groups": {
        "whitespace": 1,
        "id": 3,
        "text": 6,
        "keyword": 4,
        "condition": 2,
        "cond_pfx": 5
    }
}

>>> mo, _id = ACT_LP.match('# * call `process_parts #''a3 :| #red')
>>> printf(mo.groups())
('', '* call `process_parts ', 'a3', ':|', '#red', None)

>>> mo, _id = ACT_LP.match('        something = more #''a1 :')
>>> printf(mo.groups())
('        ', 'something = more ', 'a1', ':', None, None)

>>> mo, _id = ACT_LP.match('        something = more #''a1 ::')
>>> if mo: printf(mo.groups())

>>> mo, _id = ACT_LP.match('        something = more #''a1 :-')
>>> if mo: printf(mo.groups())

>>> mo, _id = ACT_LP.match('        something = more #''a1 :;')
>>> printf(mo.groups())
('        ', 'something = more ', 'a1', ':;', None, None)

>>> mo, _id = ACT_LP.match('rm -f "${top_dir}/.hgignore.new" #''a1 :')
>>> printf(mo.groups())
('', 'rm -f "${top_dir}/.hgignore.new" ', 'a1', ':', None, None)

.. \|:here:|

>>> printf(ACT_RX.pattern)
^(\s*)(?:(?://+|/\*+|;+|@:u?[bl]?comm_?@|--|#+|@[bl]?comm_?@|[.][.]) )?(.*)\s*(?://+|/\*+|;+|@:u?[bl]?comm_?@|--|#+|@[bl]?comm_?@|[.][.])([copduasmt][0-9]+) (:[];|<>/}]?|[];|<>/}.-])\s*(?:(#[0-9A-Za-z]+|backwards)(?:\s+|$))?(?:\s(.*)|\s*)$

>>> printf(ACT_LP.groups)
OrderedDict([('whitespace', 1), ('id', 3), ('text', 6), ('keyword', 4), ('condition', 2), ('cond_pfx', 5)])

.. _`UML 2.5 Diagram`: https://www.uml-diagrams.org/uml-25-diagrams.html
.. _`Structure Diagram`: https://www.uml-diagrams.org/uml-25-diagrams.html#structure-diagram
.. _`Structure Diagrams`: https://www.uml-diagrams.org/uml-25-diagrams.html#structure-diagram
.. _`Class Diagram`: https://www.uml-diagrams.org/class-diagrams-overview.html
.. _`Object Diagram`: https://www.uml-diagrams.org/class-diagrams-overview.html#object-diagram
.. _`Component Diagram`: https://www.uml-diagrams.org/component-diagrams.html
.. _`Deployment Diagram`: https://www.uml-diagrams.org/deployment-diagrams-overview.html
.. _`Behavior Diagram`: https://www.uml-diagrams.org/uml-25-diagrams.html#behavior-diagram
.. _`Behavior Diagrams`: https://www.uml-diagrams.org/uml-25-diagrams.html#behavior-diagram
.. _`Use Case Diagram`: https://www.uml-diagrams.org/use-case-diagrams.html
.. _`Activity Diagram`: https://www.uml-diagrams.org/activity-diagrams.html
.. _`State Machine Diagram`: https://www.uml-diagrams.org/state-machine-diagrams.html
.. _`Interaction diagram`: https://www.uml-diagrams.org/uml-25-diagrams.html#interaction-diagram
.. _`Message Sequence Diagram`: https://www.uml-diagrams.org/sequence-diagrams.html
.. _`Timing Diagram`: https://www.uml-diagrams.org/timing-diagrams.html

"""
# end note                                                            #a2

# (progn (forward-line 1) (snip-insert "py.b.future.with" t t "python") (insert "\n"))
# for python 2.5
from __future__ import with_statement

# (progn (forward-line 1) (snip-insert "py.main.pyramid.activate" t t "py") (insert ""))

# --------------------------------------------------
# |||:sec:||| COMPATIBILITY
# --------------------------------------------------

import sys
# (progn (forward-line 1) (snip-insert "py.b.printf" t t "py") (insert "\n"))
# adapted from http://www.daniweb.com/software-development/python/code/217214
try:
    printf = eval("print") # python 3.0 case
except SyntaxError:
    printf_dict = dict()
    try:
        exec("from __future__ import print_function\nprintf=print", printf_dict)
        printf = printf_dict["printf"] # 2.6 case
    except SyntaxError:
        def printf(*args, **kwd): # 2.4, 2.5, define our own Print function
            fout = kwd.get("file", sys.stdout)
            w = fout.write
            if args:
                w(str(args[0]))
            sep = kwd.get("sep", " ")
            for a in args[1:]:
                w(sep)
                w(str(a))
            w(kwd.get("end", "\n"))
    del printf_dict

# (progn (forward-line 1) (snip-insert "py.b.sformat" t t "py") (insert "\n"))
try:
    ('{0}').format(0)
    def sformat (fmtspec, *args, **kwargs):
        return fmtspec.format(*args, **kwargs)
except AttributeError:
    try:
        import stringformat
        def sformat (fmtspec, *args, **kwargs):
            return stringformat.FormattableString(fmtspec).format(
                *args, **kwargs)
    except ImportError:
        printf('error: stringformat missing. Try `easy_install stringformat`.', file=sys.stderr)

# (progn (forward-line 1) (snip-insert "py.b.isstring" t t "python") (insert "\n"))
try:
    from ws_seq_type import isstring, issequence, sequence_type, UCHAR_FMT
except ImportError:
    # (progn (forward-line 1) (snip-insert "py.f.isstring" t t "py") (insert "\n"))
    exec('''
    def isstring(obj):
        return isinstance(obj, basestring)
    '''.strip())
    try:
        isstring("")
        UCHAR_FMT = 'u"{0}u{1:04x}"'
    except NameError:
        def isstring(obj):
            return isinstance(obj, str) or isinstance(obj, bytes)
        UCHAR_FMT = '"{0}u{1:04x}"'
    # (progn (forward-line 1) (snip-insert "py.f.issequence" t t "py") (insert "\n"))
    def issequence(arg, or_dict=False, or_seq=True):           # ||:fnc:||
        if not isstring(arg):
            if hasattr(arg, 'items'):
                return or_dict
            if hasattr(arg, '__getitem__'):
                return True
            if hasattr(arg, '__iter__'):
                return or_seq
        return False
    # (progn (forward-line 1) (snip-insert-mode "py.f.sequence_type" t) (insert "\n"))
    _st_strg = (True,  False, False, False)
    _st_list = (False, True,  False, False)
    _st_dict = (False, False, True,  False)
    _st_seq  = (False, False, False, True)
    _st_none = (False, False, False, False)
    def sequence_type(value):                                  # ||:fnc:||
        if isstring(value):
            return _st_strg
        if hasattr(value, 'items'):
            return _st_dict
        if hasattr(value, '__getitem__'):
            return _st_list
        if hasattr(value, '__iter__'):
            return _st_seq
        return _st_none

# (progn (forward-line 1) (snip-insert-mode "py.f.uchar" t) (insert "\n"))
def uchar(num):
    '''Make UNICODE character.'''
    return eval(sformat(UCHAR_FMT,'\\', num))

# (progn (forward-line 1) (snip-insert "py.b.dict.items" t t "py") (insert "\n"))
try:
    getattr(dict(), 'iteritems')
except AttributeError:
    ditems  = lambda d: getattr(d, 'items')()
    dkeys   = lambda d: getattr(d, 'keys')()
    dvalues = lambda d: getattr(d, 'values')()
else:
    ditems  = lambda d: getattr(d, 'iteritems')()
    dkeys   = lambda d: getattr(d, 'iterkeys')()
    dvalues = lambda d: getattr(d, 'itervalues')()

# (progn (forward-line 1) (snip-insert "py.b.xrange" t t "py") (insert "\n"))
# `xrange` returns a generator, which `range` already does for python3
# note: use l.. and g.. to get list/generator versions
try:
    xrange(0)
    lrange = lambda *args, **kwargs: range(*args, **kwargs)
except NameError:
    xrange = range
    lrange = lambda *args, **kwargs: list(range(*args, **kwargs))
grange = xrange

# `xfilter` returns a list, `filter` may return a generator. This is
# different from the range/xrange semantics!
if isinstance(filter(str, []), list):
    xfilter = filter
    gfilter = lambda _f, _s, *args, **kwargs: (_e for _e in _s if _f(_e))
else:
    xfilter = lambda *args, **kwargs: list(filter(*args, **kwargs))
    gfilter = filter
lfilter = xfilter

# `xmap` returns a list, `map` may return a generator. This is
# different from the range/xrange semantics!
if isinstance(map(str, []), list):
    xmap = map
    gmap = lambda _f, _s, *args, **kwargs: (_f(_e) for _e in _s)
else:
    xmap = lambda *args, **kwargs: list(map(*args, **kwargs))
    gmap = map
lmap = xmap

# `long` is gone in python3
try:
    isinstance(int, long)
except NameError:
    long = int

# (progn (forward-line 1) (snip-insert "py_f.lfind" t t "python") (insert "\n"))
def lfind(l, elt):
    try:
        return l.index(elt)
    except ValueError:
        return -1

import os
import re

# --------------------------------------------------
# |||:sec:||| CONFIGURATION
# --------------------------------------------------

__all__ = []
__all_internal__ = []

# (progn (forward-line 1) (snip-insert "py.b.dbg.def" t t "python") (insert ""))
dbg_fwid = globals().get('dbg_fwid', 15)

# (progn (forward-line 1) (snip-insert "py.b.canonize.module" t t "python") (insert ""))
def _canonize_module_(module_or_name, full=None, drop=None):
    if isstring(module_or_name):
        module = sys.modules[module_or_name]
    else:
        module = module_or_name
    module_name = module.__name__
    # flag for __main__
    _is_main_ = (module_name == '__main__')
    if not hasattr(module, '_is_main_'):
        module._is_main_ = _is_main_
    if not full:
        return _is_main_
    # module file name -> canonical name
    try:
        mfile = module.__file__
    except AttributeError:
        return
    canon_name_ = mfile
    canon_name_ = os.path.basename(canon_name_)
    canon_name_, _ext = os.path.splitext(canon_name_)
    # adhoc compiliation xx_.py -> xx.py
    if canon_name_.endswith('_') and not canon_name_.endswith('__'):
        canon_name_ = canon_name_[:-1]
    canon_name_ = re.sub('[^0-9A-Za-z_]+', '_', canon_name_)
    # find parent module |:check:| distutils/pkg_resources?
    mdir = os.path.abspath(os.path.dirname(mfile))
    mparts = []
    while mdir and os.path.exists(os.path.join(mdir, '__init__.py')):
        mdir, pfx = os.path.split(mdir)
        mparts.insert(0, pfx)
    parent = '.'.join(mparts)
    if canon_name_ != '__init__':
        mparts.append(canon_name_)
    if drop:
        mparts = mparts[:-drop]
    canon_name = '.'.join(mparts)
    if module_name != canon_name:
        if parent != canon_name or drop: # |:check:| why?
            if parent:
                # fix parent module
                exec('import ' + parent)
                if parent in (sys.modules):
                    setattr(sys.modules[parent], canon_name_, module)
            sys.modules[canon_name] = module
        module.__name__ = canon_name
       # adjust module members
        for t in dvalues(vars(module)):
            try:
                if '__module__' in vars(t) and t.__module__ == module_name:
                    t.__module__ = canon_name
            except TypeError:
                pass
    return _is_main_

#_canonize_module_(__name__, __name__ == '__main__')

# (progn (forward-line 1) (snip-insert "py.b.strings" t t "py") (insert "\n"))
# (progn (forward-line 1) (snip-insert "py.f.strclean" t t "py") (insert "\n"))
# (progn (forward-line 1) (snip-insert "py.b.logging" t t "python") (insert ""))
# (progn (forward-line 1) (snip-insert "py.b.ordereddict" t t "py") (insert "\n"))
# (progn (forward-line 1) (snip-insert "py.b.dbg.setup" t t "py") (insert "\n"))
# (progn (forward-line 1) (snip-insert "py.main.project.libdir" t t "py") (insert "\n"))
# (progn (forward-line 1) (snip-insert "py.main.sql.alchemy" t t "py") (insert "\n"))
# (progn (forward-line 1) (snip-insert "py.main.sql.ws" t t "py") (insert "\n"))

# @:adhoc_run_time:@
import pyjsmo                                               # @:adhoc:@

# (progn (forward-line 1) (snip-insert "py.b.posix" t t "py") (insert "\n"))
# (progn (forward-line 1) (snip-insert "py.b.os.system.sh" t t "py") (insert "\n"))
# (progn (forward-line 1) (snip-insert "py.b.prog.path" t t "py") (insert "\n"))
# (progn (forward-line 1) (snip-insert "py_b.line-loop" t t "py") (insert "\n"))
# (progn (forward-line 1) (snip-insert "py_b.table_standalone" t t "python") (insert ""))

# (progn (forward-line 1) (snip-insert "py.wsrfid.pylons.imports" t t "python") (insert ""))
# (progn (forward-line 1) (snip-insert "py.main.wsgi.get.app" t t "python") (insert ""))

# (progn (forward-line 1) (snip-insert "py_wsrfid.config_delayed" t t "python") (insert "\n"))
# (progn (forward-line 1) (snip-insert "py_b.wsrfid.config_translate_shortcuts" t t "python") (insert "\n"))

# (progn (forward-line 1) (snip-insert "py_b.dba_imports" t t "python") (insert "\n"))
# (progn (forward-line 1) (snip-insert "py_b.dba_datainit" t t "python") (insert "\n"))
# (progn (forward-line 1) (snip-insert "py_jsmo.imports" t t "python") (insert "\n"))

from pyjsmo.result import *
from pyjsmo.translate import trans_list_comma

# |:here:|

# --------------------------------------------------
# |||:sec:||| DATA
# --------------------------------------------------

DIAGRAM_HEADER = '''
@startuml /' @ld-id@ '/
skinparam padding 1
'''.strip()

DIAGRAM_FOOTER = '''
@enduml
'''.strip()

DELIMITERS = dict(((_d, _d) for _d in ('"', "'")))

IGNORE_BASES = ['object', 'pyjsmo.PyJsMo', 'PyJsMo']

REPLACEMENTS = dict()
REPLACEMENTS_STD = dict()

# left to right direction                                             #c0
# --------------------------------------------------
# |||:sec:||| CLASSES                                                 #c0
# --------------------------------------------------

__all__.append('LineDiversion')
[docs]class LineDiversion(list): #c0 ||:cls:|| r""" .. \|:here:| """ type = None #c0 id_ = None #c0 header = '' #c0 footer = '' #c0 def __init__(self, *args, **kwargs): # |:mth:| super(LineDiversion, self).__init__(*args, **kwargs)
[docs] def open_partition(self, text): #c0 |:mth:| r""" :returns: self for chaining. """ self.close_partition() return self
[docs] def close_partition(self): #c0 |:mth:| r""" :returns: self for chaining. """ return self
[docs] def finish(self): #c0 |:mth:| r""" :returns: self for chaining. """ return self.close_partition()
# -------------------------------------------------- # |||:sec:||| DIAGRAM CLASSES # -------------------------------------------------- DIAGRAM_LINE_PARTS = ( 'indent', 'text', 'sformat', 'kw', 'cond_pfx', 'condr', 'rest', 'mapped_kw', 'kw_sep', 'cond', 'kw_cont_sep', 'mapped_kw_cont', ) # (progn (forward-line -1) (insert "\n") (snip-insert "py.s.meth" t t "py") (backward-symbol-tag 1 "fillme" "::")) # (progn (forward-line -2) (insert "\n") (snip-insert "py.s.property" t t "py") (backward-symbol-tag 1 "fillme" "::")) # } #c0 __all__.append('Diagram')
[docs]class Diagram(LineDiversion): #c0 ||:cls:|| r""" .. \|:here:| """ opt_show_bases = True opt_ignore_bases = IGNORE_BASES kw_map = dict(( #c0 (':;' , ('', '') ), (':|' , ('', '') ), (':<' , ('', '') ), (':>' , ('', '') ), (':/' , ('', '') ), (':]' , ('', '') ), (':}' , ('', '') ), (':' , ('', '') ), ('-' , ('', '') ), ('.' , ('', '') ), (';' , ('', '') ), ('|' , ('', '') ), ('<' , ('', '') ), ('>' , ('', '') ), ('/' , ('', '') ), (']' , ('', '') ), ('}' , ('', '') ), ('class' , ('class', ' "') ), ('def' , ('', '') ), ('done' , ('endwhile', ' ')), ('elif' , ('elseif', ' (') ), ('else' , ('else', ' ') ), ('elseif', ('elseif', ' (') ), ('elsif' , ('elseif', ' (') ), ('fi' , ('endif', ' ') ), ('for' , ('while', ' (for ') ), ('if' , ('if', ' (') ), ('while' , ('while', ' (') ), ('break' , ('break', ' ') ), )) kw_cont_map = dict(( #c0 (':;' , ('', '') ), (':|' , ('', '') ), (':<' , ('', '') ), (':>' , ('', '') ), (':/' , ('', '') ), (':]' , ('', '') ), (':}' , ('', '') ), (':' , ('', '') ), ('-' , ('', '') ), ('.' , ('', '') ), (';' , ('', '') ), ('|' , ('', '') ), ('<' , ('', '') ), ('>' , ('', '') ), ('/' , ('', '') ), (']' , ('', '') ), ('}' , ('', '') ), ('class' , ('', '" {') ), ('def' , ('', '') ), ('done' , ('', '') ), ('elif' , (') then', '')), ('elseif', (') then', '')), ('elsif' , (') then', '')), ('for' , (')', '') ), ('if' , (') then', '')), ('while' , (')', '') ), ('break' , ('', '') ), )) header = ''.join(( #c0 DIAGRAM_HEADER, ''' ''')).strip() footer = ''.join(( #c0 DIAGRAM_FOOTER, ''' ''')).strip() in_partition = False #c0 partition_format = 'partition "{0}" {1} {{' #c0 association = [] #c0 inheritance = [] #c0 def __init__(self, *args, **kwargs): # |:mth:| super(Diagram, self).__init__(*args, **kwargs) self.associations = [] self.inheritance = []
[docs] def open_partition(self, text): #c0 |:mth:| r""" :returns: self for chaining. """ super(Diagram, self).open_partition(text) text = text.strip() color = '' _mo = re.search('(.*)(#[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]([0-9a-f][0-9a-f])?)(?i)', text) if _mo: text = _mo.group(1).strip() color = _mo.group(2) self.append(sformat(self.partition_format, text, color)) self.in_partition = True return self
[docs] def close_partition(self): #c0 |:mth:| r""" :returns: self for chaining. """ super(Diagram, self).close_partition() if self.inheritance: self.extend((' '.join(_i) for _i in self.inheritance)) del(self.inheritance[:]) if self.in_partition: self.append('}') self.in_partition = False return self
[docs] def process_parts(self, part_map): # |:mth:| #c0 r""" :returns: new/altered part_map. >>> check_def = 'SomeClass ( with,multiple, inheritance )' >>> mo = re.match(CLASS_DEF_RX, check_def) >>> if mo: printf(sformat("{0}//{1}//", mo.groups(), check_def[mo.end(0):])) ('SomeClass', 'with,multiple, inheritance ')//// >>> class Check(): ... pass >>> check_def = 'SomeClass ()' >>> mo = re.match(CLASS_DEF_RX, check_def) >>> if mo: printf(sformat("{0}//{1}//", mo.groups(), check_def[mo.end(0):])) ('SomeClass', '')//// >>> check_def = 'SomeClass' >>> mo = re.match(CLASS_DEF_RX, check_def) >>> if mo: printf(sformat("{0}//{1}//", mo.groups(), check_def[mo.end(0):])) ('SomeClass', None)//// """ if part_map.get('mapped_kw') == 'class': class_def = part_map.get('cond') mo = re.match(CLASS_DEF_RX, class_def) if mo: name = mo.group(1) if self.opt_show_bases: bases = (mo.group(2) or '').strip().replace(' ', '').split(',') self.inheritance.extend(((_b, '<|--', name) for _b in bases if _b and _b not in self.opt_ignore_bases)) else: name = class_def cond = [] text = TAG_RX.sub('', part_map.get('text') or '') part_map['text'] = '' mo = re.search('"[^"]+"', text) if mo: _pre = text[:mo.start(0)].strip() _post = text[mo.end(0):].strip() cond.extend((mo.group(0), 'as', name, _pre, _post)) else: cond.extend((name, text)) part_map['cond'] = ' '.join((_p for _p in cond if _p)) part_map['kw_sep'] = ' ' part_map['kw_cont_sep'] = ' {' elif part_map.get('kw') == 'def': _text_list = [] _condr = part_map.get('condr') _mo = re.match('([^#]*)#+(.*)', _condr) if _mo: _condr = _mo.group(1).strip() part_map['condr'] = _condr _text_list.append(_mo.group(2).strip()) _text = ' '.join((_t for _t in (_text_list[0], part_map.get('text')) if _t)) part_map['text'] = _text # - remove trailing colon, semicolon _cond = re.sub('\\s*[:;]$', '', _condr) part_map['cond'] = _cond # |||:here:||||:todo:| more intelligent attribute handling return part_map
# (progn (forward-line -1) (insert "\n") (snip-insert "py.s.meth" t t "py") (backward-symbol-tag 1 "fillme" "::")) # (progn (forward-line -2) (insert "\n") (snip-insert "py.s.property" t t "py") (backward-symbol-tag 1 "fillme" "::")) # } #c0 __all__.append('DiagramStructure')
[docs]class DiagramStructure(Diagram): #c0 ||:cls:|| r"""LineDiversion for Structure Diagram. .. \|:here:| """ header = ''.join(( #c0 Diagram.header, ''' ''')).strip() footer = ''.join(( #c0 Diagram.footer, ''' ''')).strip() def __init__(self, *args, **kwargs): # |:mth:| super(DiagramStructure, self).__init__(*args, **kwargs)
# (progn (forward-line -1) (insert "\n") (snip-insert "py.s.meth" t t "py") (backward-symbol-tag 1 "fillme" "::")) # (progn (forward-line -2) (insert "\n") (snip-insert "py.s.property" t t "py") (backward-symbol-tag 1 "fillme" "::")) # } #c0 __all__.append('DiagramClass')
[docs]class DiagramClass(DiagramStructure): #c0 ||:cls:|| r"""LineDiversion for Class Diagram. .. \|:here:| """ type = 'c' #c0 c header = ''.join(( #c0 DiagramStructure.header, ''' ''')).strip() footer = ''.join(( #c0 DiagramStructure.footer, ''' ''')).strip() partition_format = 'frame "{0}" {1} {{' #c0 def __init__(self, *args, **kwargs): # |:mth:| super(DiagramClass, self).__init__(*args, **kwargs)
# (progn (forward-line -1) (insert "\n") (snip-insert "py.s.meth" t t "py") (backward-symbol-tag 1 "fillme" "::")) # (progn (forward-line -2) (insert "\n") (snip-insert "py.s.property" t t "py") (backward-symbol-tag 1 "fillme" "::")) # } #c0 __all__.append('DiagramObject')
[docs]class DiagramObject(DiagramStructure): #c0 ||:cls:|| r"""LineDiversion for Object Diagram. .. \|:here:| """ type = 'o' #c0 header = ''.join(( DiagramStructure.header, ''' ''')).strip() footer = ''.join(( DiagramStructure.footer, ''' ''')).strip() def __init__(self, *args, **kwargs): # |:mth:| super(DiagramObject, self).__init__(*args, **kwargs)
# (progn (forward-line -1) (insert "\n") (snip-insert "py.s.meth" t t "py") (backward-symbol-tag 1 "fillme" "::")) # (progn (forward-line -2) (insert "\n") (snip-insert "py.s.property" t t "py") (backward-symbol-tag 1 "fillme" "::")) # } #c0 __all__.append('DiagramComponent')
[docs]class DiagramComponent(DiagramStructure): #c0 ||:cls:|| r"""LineDiversion for Component Diagram. .. \|:here:| """ type = 'p' #c0 header = ''.join(( DiagramStructure.header, ''' ''')).strip() footer = ''.join(( DiagramStructure.footer, ''' ''')).strip() def __init__(self, *args, **kwargs): # |:mth:| super(DiagramComponent, self).__init__(*args, **kwargs)
# (progn (forward-line -1) (insert "\n") (snip-insert "py.s.meth" t t "py") (backward-symbol-tag 1 "fillme" "::")) # (progn (forward-line -2) (insert "\n") (snip-insert "py.s.property" t t "py") (backward-symbol-tag 1 "fillme" "::")) # } #c0 __all__.append('DiagramDeployment')
[docs]class DiagramDeployment(DiagramStructure): #c0 ||:cls:|| r"""LineDiversion for Deployment Diagram. .. \|:here:| """ type = 'd' #c0 header = ''.join(( DiagramStructure.header, ''' ''')).strip() footer = ''.join(( DiagramStructure.footer, ''' ''')).strip() def __init__(self, *args, **kwargs): # |:mth:| super(DiagramDeployment, self).__init__(*args, **kwargs)
# (progn (forward-line -1) (insert "\n") (snip-insert "py.s.meth" t t "py") (backward-symbol-tag 1 "fillme" "::")) # (progn (forward-line -2) (insert "\n") (snip-insert "py.s.property" t t "py") (backward-symbol-tag 1 "fillme" "::")) # } #c0 __all__.append('DiagramBehavior')
[docs]class DiagramBehavior(Diagram): #c0 ||:cls:|| r"""LineDiversion for Behavior Diagram. .. \|:here:| """ header = ''.join(( Diagram.header, ''' ''')).strip() footer = ''.join(( Diagram.footer, ''' ''')).strip() def __init__(self, *args, **kwargs): # |:mth:| super(DiagramBehavior, self).__init__(*args, **kwargs)
# (progn (forward-line -1) (insert "\n") (snip-insert "py.s.meth" t t "py") (backward-symbol-tag 1 "fillme" "::")) # (progn (forward-line -2) (insert "\n") (snip-insert "py.s.property" t t "py") (backward-symbol-tag 1 "fillme" "::")) # } #c0 __all__.append('DiagramUseCase')
[docs]class DiagramUseCase(DiagramBehavior): #c0 ||:cls:|| r"""LineDiversion for Use Case Diagram. .. \|:here:| """ type = 'u' #c0 header = ''.join(( DiagramBehavior.header, ''' ''')).strip() footer = ''.join(( DiagramBehavior.footer, ''' ''')).strip() def __init__(self, *args, **kwargs): # |:mth:| super(DiagramUseCase, self).__init__(*args, **kwargs)
# (progn (forward-line -1) (insert "\n") (snip-insert "py.s.meth" t t "py") (backward-symbol-tag 1 "fillme" "::")) # (progn (forward-line -2) (insert "\n") (snip-insert "py.s.property" t t "py") (backward-symbol-tag 1 "fillme" "::")) # } #c0 __all__.append('DiagramActivity')
[docs]class DiagramActivity(DiagramBehavior): #c0 ||:cls:|| r"""LineDiversion for Activity Diagram. .. \|:here:| """ type = 'a' #c0 kw_map = dict(( (':;', (':', '')), (':|', (':', '')), (':<', (':', '')), (':>', (':', '')), (':/', (':', '')), (':]', (':', '')), (':}', (':', '')), (':' , (':', '')), ('-' , ('', '') ), ('.' , ('', '') ), (';' , ('', '') ), ('|' , ('', '') ), ('<' , ('', '') ), ('>' , ('', '') ), ('/' , ('', '') ), (']' , ('', '') ), ('}' , ('', '') ), )) kw_cont_map = dict(( (':;', (';', '')), (':|', ('|', '')), (':<', ('<', '')), (':>', ('>', '')), (':/', ('/', '')), (':]', (']', '')), (':}', ('}', '')), (':' , ('', '') ), ('-' , ('', '') ), ('.' , ('', '') ), (';' , (';', '')), ('|' , ('|', '')), ('<' , ('<', '')), ('>' , ('>', '')), ('/' , ('/', '')), (']' , (']', '')), ('}' , ('}', '')), )) header = ''.join(( DiagramBehavior.header, ''' ''')).strip() footer = ''.join(( DiagramBehavior.footer, ''' ''')).strip() def __init__(self, *args, **kwargs): # |:mth:| super(DiagramActivity, self).__init__(*args, **kwargs)
# (progn (forward-line -1) (insert "\n") (snip-insert "py.s.meth" t t "py") (backward-symbol-tag 1 "fillme" "::")) # (progn (forward-line -2) (insert "\n") (snip-insert "py.s.property" t t "py") (backward-symbol-tag 1 "fillme" "::")) # } #c0 __all__.append('DiagramStateMachine')
[docs]class DiagramStateMachine(DiagramBehavior): #c0 ||:cls:|| r"""LineDiversion for State Machine Diagram. .. \|:here:| """ type = 's' #c0 header = ''.join(( DiagramBehavior.header, ''' ''')).strip() footer = ''.join(( DiagramBehavior.footer, ''' ''')).strip() def __init__(self, *args, **kwargs): # |:mth:| super(DiagramStateMachine, self).__init__(*args, **kwargs)
# (progn (forward-line -1) (insert "\n") (snip-insert "py.s.meth" t t "py") (backward-symbol-tag 1 "fillme" "::")) # (progn (forward-line -2) (insert "\n") (snip-insert "py.s.property" t t "py") (backward-symbol-tag 1 "fillme" "::")) # } #c0 __all__.append('DiagramInteraction')
[docs]class DiagramInteraction(DiagramBehavior): #c0 ||:cls:|| r"""LineDiversion for Interaction Diagram. .. \|:here:| """ header = ''.join(( DiagramBehavior.header, ''' ''')).strip() footer = ''.join(( DiagramBehavior.footer, ''' ''')).strip() def __init__(self, *args, **kwargs): # |:mth:| super(DiagramInteraction, self).__init__(*args, **kwargs)
# (progn (forward-line -1) (insert "\n") (snip-insert "py.s.meth" t t "py") (backward-symbol-tag 1 "fillme" "::")) # (progn (forward-line -2) (insert "\n") (snip-insert "py.s.property" t t "py") (backward-symbol-tag 1 "fillme" "::")) # } #c0 __all__.append('DiagramMessageSequence')
[docs]class DiagramMessageSequence(DiagramInteraction): #c0 ||:cls:|| r"""LineDiversion for Message Sequence Diagram. .. \|:here:| """ type = 'm' #c0 header = ''.join(( DiagramInteraction.header, ''' ''')).strip() footer = ''.join(( DiagramInteraction.footer, ''' ''')).strip() def __init__(self, *args, **kwargs): # |:mth:| super(DiagramMessageSequence, self).__init__(*args, **kwargs)
# (progn (forward-line -1) (insert "\n") (snip-insert "py.s.meth" t t "py") (backward-symbol-tag 1 "fillme" "::")) # (progn (forward-line -2) (insert "\n") (snip-insert "py.s.property" t t "py") (backward-symbol-tag 1 "fillme" "::")) # } #c0 __all__.append('DiagramTiming')
[docs]class DiagramTiming(DiagramInteraction): #c0 ||:cls:|| r"""LineDiversion for Timing Diagram. .. \|:here:| """ type = 't' #c0 header = ''.join(( DiagramInteraction.header, ''' ''')).strip() footer = ''.join(( DiagramInteraction.footer, ''' ''')).strip() def __init__(self, *args, **kwargs): # |:mth:| super(DiagramTiming, self).__init__(*args, **kwargs)
# (progn (forward-line -1) (insert "\n") (snip-insert "py.s.meth" t t "py") (backward-symbol-tag 1 "fillme" "::")) # (progn (forward-line -2) (insert "\n") (snip-insert "py.s.property" t t "py") (backward-symbol-tag 1 "fillme" "::")) # } #c0 DIAGRAMS = dict( ((_obj.type, _obj) for _name, _obj in ditems(globals()) if _name.startswith('Diagram') and getattr(_obj, 'type', None) )) # printe=printf # printe(sformat("# "":DBG: {1:<{0}s}: ]{2!s}[", dbg_fwid, "DIAGRAMS", (DIAGRAMS))) # exit(0) # (progn (forward-line -1) (insert "\n") (snip-insert "py.s.class" t t "py") (backward-symbol-tag 1 "fillme" "::")) # -------------------------------------------------- # |||:sec:||| REGULAR EXPRESSIONS (SNIAP) # -------------------------------------------------- # (progn (forward-line 1) (snip-insert "py_snip.rx" t t "py") (insert "\n")) # All snippet tags SNIAP_RX='\\|\\|<-sn([ia])p->\\|\\|' # Command tags at beginning of line preceeded at most by one word SNIAP_PFX_RX='^(([^ @][^ ]*|@:[^ ]*|\\.\\. \\\\)[ ]*|)' + SNIAP_RX # Command tags at beginning of line preceeded at most by one word, # which cannot begin with an alpha-numeric character SNIAP_PFX_RX1='^(([^ @0-9A-Za-z][^ ]*|@:[^ ]*|\\.\\. \\\\)[ ]*|)' + SNIAP_RX # The regular expression used on 2012-01-11 03:11:54 (which may have # changed since then). This Regexp is automatically generated from all # defined comment start skips. # Perl: '//+|/\*+|;+|\.\. |\.\. \\|\@:[bl]?comm_?\@|--|<!--|#+|\@[bl]?comm_?\@' SNIAP_PFX_RXX='^((//+|/\\*+|;+|\\.\\. |\\.\\. \\\\|@:[bl]?comm_?@|--|<!--|#+|@[bl]?comm_?@) *|)' + SNIAP_RX # Catch indented command tags (especially for beg/end tags, but --no-indent is better in this case) SNIAP_PFX_RXI='^(([ ]*[^ ]*|\\.\\. \\\\)[ ]*|)' + SNIAP_RX # Snippet command and arguments (remove comment end before matching # for arguments) SNIP_CMD_RX=' *([A-Za-z_][-0-9A-Za-z_]*) *(.*)' # -------------------------------------------------- # |||:sec:||| REGULAR EXPRESSIONS # -------------------------------------------------- # Generic comment end with PUML and HTML comments COMMENT_RX ='(?://+|/[*\']+|;+|@:u?[bl]?comm_?@|--|<!--|#+|@[bl]?comm_?@|\\.\\. |\\.\\. \\\\)' COMMENT_TYPE_RX='(?://+|/[*\']+|;+|@:u?[bl]?comm_?@|--|<!--|#+|@[bl]?comm_?@|[.][.])' COMMENT_END_RX_='@:(((_|)(b|)comm)|((b|)comme))@|[*\']/|-->|`' # Generic comment end with HTML comments COMMENT_RX ='(?://+|/\\*+|;+|@:u?[bl]?comm_?@|--|<!--|#+|@[bl]?comm_?@|\\.\\. |\\.\\. \\\\)' COMMENT_TYPE_RX='(?://+|/\\*+|;+|@:u?[bl]?comm_?@|--|<!--|#+|@[bl]?comm_?@|[.][.])' COMMENT_END_RX_='@:(((_|)(b|)comm)|((b|)comme))@|[*]/|-->|`' # generic comment RXs with PUML, w/o HTML comments COMMENT_RX ='(?://+|/[*\']+|;+|@:u?[bl]?comm_?@|--|#+|@[bl]?comm_?@|\\.\\. |\\.\\. \\\\)' COMMENT_TYPE_RX='(?://+|/[*\']+|;+|@:u?[bl]?comm_?@|--|#+|@[bl]?comm_?@|[.][.])' COMMENT_END_RX_='@:(((_|)(b|)comm)|((b|)comme))@|[*\']/|`' # generic comment RXs w/o PUML and HTML comments # since a HTML comment end is also a valid association `-->`. Diagrams # embedded in HTML documents can also be annotated with a hash `#` # comment start. COMMENT_RX ='(?://+|/\\*+|;+|@:u?[bl]?comm_?@|--|#+|@[bl]?comm_?@|\\.\\. |\\.\\. \\\\)' COMMENT_TYPE_RX='(?://+|/\\*+|;+|@:u?[bl]?comm_?@|--|#+|@[bl]?comm_?@|[.][.])' COMMENT_END_RX_='@:(((_|)(b|)comm)|((b|)comme))@|[*]/|`' COMMENT_END_RX=re.compile("\\s*" + COMMENT_END_RX_ + "\\s*$") COMMENT_TRAIL_RX = re.compile(COMMENT_TYPE_RX + '+$') SYMBOL_RX = '[A-Za-z_][0-9A-Za-z_]*' XSYMBOL_RX = '[-A-Za-z_][-0-9A-Za-z_]*' DOT_SYMBOL_RX = '[A-Za-z_][0-9A-Za-z_.]*' SECTION_RX = re.compile('(\\s*)\|\|*:sec:\|\|*\\s*') TAG_RX = re.compile('\\s*\|\|*:[^:]*:\|\|*\\s*') DIAGRAM_TYPE_RX = '[copduasmt]' OPT_TRAILER_RX = '(?:\\s(.*)|\\s*)$' OPT_TRAILER_TAG_RX = '(?:' + TAG_RX.pattern + ')*$' CLASS_DEF_RX = '(' + SYMBOL_RX + ')\\s*(?:[(]\\s*(?:(' + SYMBOL_RX + ')\\s*(?:,\\s*(' + SYMBOL_RX + ')\\s*)*)?[)])?' CLASS_DEF_RX = '(' + SYMBOL_RX + ')\\s*(?:[(]\\s*((?:' + DOT_SYMBOL_RX + '|[,]|\\s)*)[)])?' REST_COMMENT_START_RX = re.compile('^\\s*[.][.]\\s?') REST_INLINE_COMMENT_RX = re.compile(':rem:`([^`]*)`') # -------------------------------------------------- # |||:sec:||| LINE PARSERS # -------------------------------------------------- from pyjsmo.translate import trans_re_flags from pyjsmo.translate import trans_rx_repr STD_GROUPS = pyjsmo.OrderedDict(( (_k, None) for _k in ( 'whitespace', 'id', 'text', 'keyword', 'condition',) )) __all__.append('LineParser')
[docs]class LineParser(pyjsmo.PyJsMo): #c0 ||:cls:|| r""" >>> lp = LineParser() >>> printf(lp) { "rx": [ null, "0" ], "groups": { "whitespace": null, "id": null, "text": null, "keyword": null, "condition": null } } >>> printf(lp._pyjsmo_x_rx) None >>> lp.rx = re.compile('some', re.I | re.M | re.U) >>> printf(lp) { "rx": [ "some", "re.IGNORECASE|re.MULTILINE|re.UNICODE" ], "groups": { "whitespace": null, "id": null, "text": null, "keyword": null, "condition": null } } >>> printf(trans_rx_repr(lp._pyjsmo_x_rx)) #doctest: +ELLIPSIS re.compile('some', re.IGNORECASE|re.MULTILINE|re.UNICODE) >>> lp.rx = ("some where", "re.IGNORECASE|re.UNICODE") >>> printf(lp) { "rx": [ "some where", "re.IGNORECASE|re.UNICODE" ], "groups": { "whitespace": null, "id": null, "text": null, "keyword": null, "condition": null } } >>> printf(trans_rx_repr(lp._pyjsmo_x_rx)) #doctest: +ELLIPSIS re.compile('some where', re.IGNORECASE|re.UNICODE) """ _pyjsmo_versions = [ (1, {'bases': [0], 'order': [ 'rx', #c0 'groups', #c0 ], 'defaults': pyjsmo.OrderedDict(( ('rx', None), ('groups', None), )), 'amap': [], 'expand': None, }), ] def __init__(self, *args, **kwargs): # |:mth:| super(LineParser, self).__init__(*args, **kwargs) if self.groups is None: self.groups = pyjsmo.OrderedDict(STD_GROUPS) # http://adam.gomaa.us/blog/2008/aug/11/the-python-property-builtin/ _pyjsmo_x_rx = None def _get_rx(self): # |:mth:| r"""Programmatic property.""" value = self._pyjsmo_x_rx return ( getattr(value, 'pattern', value), trans_re_flags(getattr(value, 'flags', None)) ) def _set_rx(self, value): # |:mth:| flags = None if issequence(value): flags = value[1] if isstring(flags): flags = eval(flags) value = value[0] if isstring(value): value = re.compile(value, flags) self._pyjsmo_x_rx = value rx = property(_get_rx, _set_rx, None, _get_rx.__doc__)
[docs] def init(self, rx, groups): #c0 |:mth:| r""" :returns: self for chaining. """ self.rx = rx _groups = pyjsmo.OrderedDict(STD_GROUPS) if groups: _groups.update(groups) self.groups = _groups return self
[docs] def match(self, line): #c0 |:mth:| r"""Match against line. :returns: match object or None. """ rx = self._pyjsmo_x_rx if not rx: return None mo = rx.search(line) if mo: id_ = mo.group(self.groups['id']) else: id_ = None return mo, id_
[docs] def split_(self, line, mo, diagram): # |:mth:| r""" :returns: part map """ # |:sec:| Split Line Into Parts #a4 # start #a4 # * split line into parts #a4 : # indent, text, keyword, condition, rest indent = line[:mo.end(self.groups.get('whitespace'))] _gtext = self.groups.get('text') text = COMMENT_TRAIL_RX.sub( '', ((_gtext and mo.group(_gtext)) or '')).strip() _gkeyword = self.groups.get('keyword') kw = ((_gkeyword and mo.group(_gkeyword)) or '').strip() _gcond_pfx = self.groups.get('cond_pfx') cond_pfx = ((_gcond_pfx and mo.group(_gcond_pfx)) or '').strip() _gcondition = self.groups.get('condition') cond = condr = COMMENT_TRAIL_RX.sub( '', ((_gcondition and mo.group(_gcondition)) or '')).rstrip() _gsformat = self.groups.get('sformat') sformat_ = ((_gsformat and mo.group(_gsformat)) or '').strip() if sformat_: _fmt = [] _rest = sformat_ while True: if _rest: _delimiter = DELIMITERS.get(_rest[0]) if _delimiter: _rest = _rest[1:] else: break _pos = _rest.find(_delimiter) if _pos >= 0: _fmt.append(_rest[:_pos]) _rest = _rest[_pos+1:] continue _fmt.append(_rest) break _fmt = ''.join(_fmt) _fmt = re.sub('^\\s*#\\s*', '', _fmt) _rest = re.sub('^\\s*,\\s*', '', _rest) _args = [re.sub('^\\s*([\'"])(.*)\\1\\s*$', '\\2', _a) for _a in trans_list_comma(_rest)] _args.extend(('' for _i in xrange(20))) sformat_ = sformat(_fmt, *_args) if kw: cond = sformat_ sformat_ = '' # remove tags from action header cond = TAG_RX.sub('', cond) cond = cond.rstrip() rest = line[mo.end(0):] # * identify condition parts #a4 - mapped_kw, kw_sep = diagram.kw_map.get( kw, KW_PLANT_MAP.get(kw, (kw, ' '))) mapped_kw_cont, kw_cont_sep = diagram.kw_cont_map.get( kw, KW_PLANT_CONT_MAP.get(kw, ('', ''))) # - remove trailing colon, semicolon #a4 - # - remove paired parentheses #a4 ; if cond: cond = re.sub('\\s*[:;]$', '', cond) cond = re.sub('^(\\s*)[(](.*)[)]\\s*$', '\\1\\2', cond) _loc = locals() part_map = pyjsmo.OrderedDict(zip(DIAGRAM_LINE_PARTS, (_loc.get(_p.replace('sformat', 'sformat_')) for _p in DIAGRAM_LINE_PARTS))) if _debug: printe("# --------------------------------------------------") printe(sformat("# "":DBG: {1:<{0}s}: ]{2!s}[", dbg_fwid, "rx", (self.rx))) printe(sformat("# "":DBG: {1:<{0}s}: ]{2!s}[", dbg_fwid, "line", (line))) printe(sformat("# "":DBG: {1:<{0}s}: ]{2!s}[", dbg_fwid, "mo.groups", (mo.groups()))) printe(sformat("# "":DBG: {1:<{0}s}: ]{2!s}[", dbg_fwid, "part_map", list(ditems(part_map)))) return part_map
[docs] def split(self, line, mo, diagram): #c0 |:mth:| r""" :returns: part map - indent - text - kw - cond_pfx - condr - rest - mapped_kw - kw_sep - cond - kw_cont_sep - mapped_kw_cont """ part_map = self.split_(line, mo, diagram) # * call `process_parts` method of `Diagram` #a4 :| part_map = diagram.process_parts(part_map) # stop #a4 return part_map
[docs] def assemble(self, part_map, diagram): #c0 |:mth:| r""" :returns: assembled line. """ # |:sec:| Assemble Line Parts #a5 # start #a5 # * assemble condition #a5 : _cond_parts = [] _cond_pfx = part_map.get('cond_pfx') or '' if _cond_pfx: _cond_parts.append(_cond_pfx) _mapped_kw = part_map.get('mapped_kw') or '' if _mapped_kw: _cond_parts.append(_mapped_kw) _cond_parts.append(part_map.get('kw_sep') or '') _cond = part_map.get('cond') or '' if _cond: _cond_parts.append(_cond) _cond_parts.append(part_map.get('kw_cont_sep') or '') _cond_parts.append(part_map.get('mapped_kw_cont') or '') _cond_text = ''.join(_cond_parts) # * assemble all parts of line #a5 ; _parts = [ part_map.get('indent') or '', _cond_text ] text = part_map.get('text') or '' if text and _cond_text: _parts.append(' ') _parts.append(text) sformat_ = part_map.get('sformat') or '' if sformat_ and (text or _cond_text): _parts.append(' ') _parts.append(sformat_) _parts.append(part_map.get('rest') or '') if _debug: printe(sformat("# "":DBG: {1:<{0}s}: ]{2!s}[", dbg_fwid, "cond_parts", (_cond_parts))) printe(sformat("# "":DBG: {1:<{0}s}: ]{2!s}[", dbg_fwid, "parts", (_parts))) line = ''.join(_parts) # stop #a5 return line
# (progn (forward-line -1) (insert "\n") (snip-insert "py.s.meth" t t "py") (backward-symbol-tag 1 "fillme" "::")) # (progn (forward-line -2) (insert "\n") (snip-insert "py.s.property" t t "py") (backward-symbol-tag 1 "fillme" "::")) # } #c0 # (progn (forward-line -1) (insert "\n") (snip-insert "py.s.class" t t "py") (backward-symbol-tag 1 "fillme" "::")) LINE_PARSERS = [] PREFIX_RX = re.compile('^(\\s*)' + COMMENT_TYPE_RX + '(' + DIAGRAM_TYPE_RX + '[0-9]+)(?:\\s|$)') PREFIX_LP = LineParser().init( PREFIX_RX, (('whitespace', 1 ), ('id' , 2 ), ('text' , None), ('keyword' , None), ('condition' , None), )) LINE_PARSERS.append(PREFIX_LP) SUFFIX_RX = re.compile('^(\\s*)' + COMMENT_TYPE_RX + ' (.*)\\s*' + COMMENT_TYPE_RX + '(' + DIAGRAM_TYPE_RX + '[0-9]+)' + OPT_TRAILER_TAG_RX) SUFFIX_LP = LineParser().init( SUFFIX_RX, (('whitespace', 1 ), ('id' , 3 ), ('text' , 2 ), ('keyword' , None), ('condition' , None), )) LINE_PARSERS.append(SUFFIX_LP) KW_PLANT_MAP = Diagram.kw_map KW_PLANT_CONT_MAP = Diagram.kw_cont_map #printf(pyjsmo.formatv.tuple_def(sorted(ditems(KW_PLANT_MAP)))) #printf() #printf(pyjsmo.formatv.tuple_def(sorted(ditems(KW_PLANT_CONT_MAP)))) KEYWORDS = [_k for _k in sorted(dkeys(KW_PLANT_MAP)) if re.match('[a-z}](?i)', _k)] KEYWORDS_RX = '|'.join((re.escape(_k) for _k in KEYWORDS)) ACTION_RX = '(:[];|<>/}]?|[];|<>/}.-])' # printe(sformat("# ||"":sec:|| some section header{0}", "")) #a1 PRINTE_SFORMAT_RX = re.compile("^(\\s*)printe\\s*[(]\\s*sformat\\s*[(]\\s*(.*)\\s*(?:[)][)]|,)\\s*" + COMMENT_TYPE_RX + '(' + DIAGRAM_TYPE_RX + '[0-9]+)\\s*' + ACTION_RX + '?' + OPT_TRAILER_RX) PRINTE_SFORMAT_LP = LineParser().init( PRINTE_SFORMAT_RX, (('whitespace', 1 ), ('sformat' , 2 ), ('id' , 3 ), ('keyword' , 4 ), ('condition' , None), )) LINE_PARSERS.append(PRINTE_SFORMAT_LP) COND_RX = re.compile('^(\\s*)(' + KEYWORDS_RX + ')(?::?|\\s)\\s*(.*)\\s*' + COMMENT_TYPE_RX + '(' + DIAGRAM_TYPE_RX + '[0-9]+)' + OPT_TRAILER_RX) COND_LP = LineParser().init( COND_RX, (('whitespace', 1), ('id' , 4), ('text' , 5), ('keyword' , 2), ('condition' , 3), )) LINE_PARSERS.append(COND_LP) ACT_RX = re.compile('^(\\s*)(?:' + COMMENT_TYPE_RX + ' )?(.*)\\s*' + COMMENT_TYPE_RX + '(' + DIAGRAM_TYPE_RX + '[0-9]+) ' + ACTION_RX + '\\s*(?:(#[0-9A-Za-z]+|backwards)(?:\\s+|$))?' + OPT_TRAILER_RX) ACT_LP = LineParser().init( ACT_RX, (('whitespace', 1), ('id' , 3), ('text' , 6), ('cond_pfx' , 5), ('keyword' , 4), ('condition' , 2), )) LINE_PARSERS.append(ACT_LP) ATTRIB_RX = re.compile('^(\\s*)(' + SYMBOL_RX + ')\\s*=\\s*(.*)\\s*' + COMMENT_TYPE_RX + '(' + DIAGRAM_TYPE_RX + '[0-9]+)' + OPT_TRAILER_RX) ATTRIB_LP = LineParser().init( ATTRIB_RX, (('whitespace', 1 ), ('id' , 4 ), ('text' , 2 ), ('keyword' , None), ('condition' , None), )) LINE_PARSERS.append(ATTRIB_LP) PYJSMO_ATTRIB_RX = re.compile("^(\\s*)['](" + SYMBOL_RX + ")['],?"'\\s*' + COMMENT_TYPE_RX + '(' + DIAGRAM_TYPE_RX + '[0-9]+)' + OPT_TRAILER_RX) PYJSMO_ATTRIB_LP = LineParser().init( PYJSMO_ATTRIB_RX, (('whitespace', 1 ), ('id' , 3 ), ('text' , 2 ), ('keyword' , None), ('condition' , None), )) LINE_PARSERS.append(PYJSMO_ATTRIB_LP) # -------------------------------------------------- # |||:sec:||| FUNCTIONS # -------------------------------------------------- __all__.append('check_line_parse')
[docs]def check_line_parse(*exprs): # ||:fnc:|| r""" :returns: prints >>> check_line_parse('# s/^+//''p') #doctest: +ELLIPSIS # -------------------------------------------------- # ||:exp:|| # s/^+//p # -------------------------------------------------- # :DBG: ACT_LP : ]--[ ]()[ # :DBG: ATTRIB_LP : ]--[ ]()[ # :DBG: COND_LP : ]--[ ]()[ # :DBG: PREFIX_LP : ]--[ ]()[ # :DBG: PRINTE_SFORMAT_LP: ]--[ ]()[ # :DBG: PYJSMO_ATTRIB_LP : ]--[ ]()[ # :DBG: SUFFIX_LP : ]--[ ]()[ >>> check_line_parse('# start #''a99') #doctest: +ELLIPSIS # -------------------------------------------------- # ||:exp:|| # start #a...99 # -------------------------------------------------- # :DBG: ACT_LP : ]--[ ]()[ # :DBG: ATTRIB_LP : ]--[ ]()[ # :DBG: COND_LP : ]--[ ]()[ # :DBG: PREFIX_LP : ]--[ ]()[ # :DBG: PRINTE_SFORMAT_LP: ]--[ ]()[ # :DBG: PYJSMO_ATTRIB_LP : ]--[ ]()[ # :DBG: SUFFIX_LP : ]a99[ ]('', 'start ', 'a99')[ >>> check_line_parse('.. start ..''a99') #doctest: +ELLIPSIS # -------------------------------------------------- # ||:exp:|| .. start ..a...99 # -------------------------------------------------- # :DBG: ACT_LP : ]--[ ]()[ # :DBG: ATTRIB_LP : ]--[ ]()[ # :DBG: COND_LP : ]--[ ]()[ # :DBG: PREFIX_LP : ]--[ ]()[ # :DBG: PRINTE_SFORMAT_LP: ]--[ ]()[ # :DBG: PYJSMO_ATTRIB_LP : ]--[ ]()[ # :DBG: SUFFIX_LP : ]a99[ ]('', 'start ', 'a99')[ >>> check_line_parse('start #''a99') #doctest: +ELLIPSIS # -------------------------------------------------- # ||:exp:|| start #a...99 # -------------------------------------------------- # :DBG: ACT_LP : ]--[ ]()[ # :DBG: ATTRIB_LP : ]--[ ]()[ # :DBG: COND_LP : ]--[ ]()[ # :DBG: PREFIX_LP : ]--[ ]()[ # :DBG: PRINTE_SFORMAT_LP: ]--[ ]()[ # :DBG: PYJSMO_ATTRIB_LP : ]--[ ]()[ # :DBG: SUFFIX_LP : ]--[ ]()[ """ _line_parsers = [] for _name, _obj in sorted(ditems(globals())): if isinstance(_obj, LineParser): _line_parsers.append((_name, _obj)) dbg_fwid = 17 for _expr in exprs: printf("# --------------------------------------------------") printf(sformat("# ||"":exp:|| {0}", _expr)) printf("# --------------------------------------------------") for _name, _lp in _line_parsers: mo, _id = _lp.match(_expr) if mo: _groups = mo.groups() else: _groups = () _id = '--' printf(sformat("# "":DBG: {1:<{0}s}: ]{2!s}[ ]{3!s}[", dbg_fwid, _name, _id, _groups))
# (progn (forward-line -1) (insert "\n") (snip-insert "py.s.func" t t "py") (backward-symbol-tag 1 "fillme" "::")) # (progn (forward-line 1) (snip-insert "py_wsrfid.customization" t t "python" " --key cust_delayed_skipx") (insert "\n")) # (progn (forward-line 1) (snip-insert "py_b.dba_setup_sql" t t "python") (insert "\n")) # (progn (forward-line 1) (snip-insert "py_b.dba_id_maps" t t "python") (insert "\n")) # (progn (forward-line 1) (snip-insert "py_b.dba_commands_init" t t "python") (insert "\n")) # (progn (forward-line 1) (snip-insert "py.wsrfid.module.route" t t "py") (insert "")) # -------------------------------------------------- # |||:sec:||| UTILITIES # -------------------------------------------------- # (progn (forward-line 1) (snip-insert "py.wsrfid.dispatch.request" t t "py") (insert "")) # (progn (forward-line 1) (snip-insert "py.f.findfile" t t "py") (insert "")) # (progn (forward-line 1) (snip-insert "py_f.add_prefix_indent" t t "python") (insert "\n")) # (progn (forward-line 1) (snip-insert "py.c.placeholder.template" t t "py") (insert "\n")) # (progn (forward-line 1) (snip-insert "py.c.key.hash.ordered.dict" t t "py") (insert "\n")) # (progn (forward-line 1) (snip-insert "py.c.progress" t t "py") (insert "\n")) # (progn (forward-line 1) (snip-insert "py.f.hl" t t "py") (insert "\n")) # (progn (forward-line 1) (snip-insert "py.f.single.quote" t t "py") (insert "\n")) # (progn (forward-line 1) (snip-insert "py.f.remove.match" t t "py") (insert "\n")) # (progn (forward-line 1) (snip-insert "py.f.printenv" t t "py") (insert "\n")) # (progn (forward-line 1) (snip-insert "py.f.uname.s" t t "py") (insert "\n")) # (progn (forward-line 1) (snip-insert "py_f.decoded_email_headers" t t "python") (insert "\n")) # (progn (forward-line 1) (snip-insert "py_f.print_utf8" t t "python") (insert "\n")) # (progn (forward-line 1) (snip-insert "py.f.printe" t t "py") (insert "")) def printe_(*args, **kwargs): kwargs['file'] = kwargs.get('file', sys.stderr) printf(*args, **kwargs) if 'printe' not in globals(): # or globals().get('_is_main_', (__name__ == '__main__')): printe = printe_ printd = printe_ printw = printe_ printx = printe_ # (progn (forward-line 1) (snip-insert "py.f.dbg.squeeze" t t "py") (insert "\n")) # (progn (forward-line 1) (snip-insert "py.f.dbg.indent" t t "py") (insert "\n")) # (progn (forward-line 1) (snip-insert "py.f.quick.dump" t t "python") (insert "\n")) # (progn (forward-line 1) (snip-insert "py_b.all.reverse" t t "python") (insert "\n")) if '__all_internal__' in globals(): import sys if 'sphinx.directives' in sys.modules: __all__[:0] = __all_internal__ __all_internal__ = list(reversed(__all_internal__)) __all__ = list(reversed(__all__)) def run(parameters): # ||:fnc:|| """Application runner, when called as __main__.""" # (progn (forward-line 1) (snip-insert "py.bf.sql.ws" t t "py") (insert "\n")) # (progn (forward-line 1) (snip-insert "py.bf.file.arg.loop" t t "py") (insert "\n")) # (progn (forward-line 1) (snip-insert "py.wsrfid.wsuvv.run" t t "py" " --key py_wsrfid.wsuvv_run --key skip_for_new") (insert "\n")) # (progn (forward-line 1) (snip-insert "py_shell.run" t t "python") (insert "\n")) # from pyjsmo.result import Result, IResult, ERR_NONE result = IResult() opt_match = parameters.match opt_file_base = parameters.file_base opt_replace = getattr(parameters, 'replace', None) opt_verbatim = getattr(parameters, 'verbatim', None) for _rep in (opt_replace or []): _sym, _val = _rep.split(None, 1) REPLACEMENTS[_sym] = _val if opt_match: opt_match_rx = re.compile(opt_match) else: opt_match_rx = None Diagram.opt_show_bases = parameters.show_bases if parameters.ignore_bases: Diagram.opt_ignore_bases = list(Diagram.opt_ignore_bases) Diagram.opt_ignore_bases.extend(parameters.ignore_bases) # |:here:| error = ERR_NONE # |:todo:| class LineDiversionParser diagrams = pyjsmo.OrderedDict() # read from standard input, #a2 : # if no arguments are given #a2 ; if not parameters.args: parameters.args.append('-') for _file in parameters.args: # setup replacement for @filepath@, @filedir@, @filename@ _filename = _file if _filename == '-': _filename = '{stdin}' _filepath = _filename else: _filepath = os.path.abspath(_filename) _filedir = os.path.dirname(_filepath) _filebase = os.path.basename(_filepath) REPLACEMENTS_STD['@filepath@'] = _filepath REPLACEMENTS_STD['@filedir@'] = _filedir REPLACEMENTS_STD['@filename@'] = _filebase # get file contents #a2 :; result.get_file_contents(_file) if result.error > ERR_NONE: #a2 (yes) error = result.error result.error = ERR_NONE continue # else #a2 lines = result.output.splitlines() cur_id = None cur_diagram = Diagram() # while (for each line) is (do) #a2 for _indx, _line in enumerate(lines): # |:todo:| move to Diagram.process_line() # * convert :rem: docstring comment #a2 : # to regular comment #a2 . if REST_INLINE_COMMENT_RX.search(_line): _line = '# ' + REST_INLINE_COMMENT_RX.sub( '#\\1', REST_COMMENT_START_RX.sub('', _line)) # * remove closing comment #a2 . # at end of line #a2 ; _line = COMMENT_END_RX.sub('', _line) # perform registered replacements #a2 :; for _sym, _val in ditems(REPLACEMENTS): _line = _line.replace(_sym, _val) # perform standard replacements #a2 :; if not opt_verbatim: for _sym, _val in ditems(REPLACEMENTS_STD): _line = _line.replace(_sym, _val) # while (for each registered line parser) is (do) #a2 for _lp in LINE_PARSERS: # get match object and diagram ID #a2 : # from LineParser.match() #a2 ; # note right #a2 # the first matching LineParser is used: #a2 # * PREFIX_LP #a2 # * SUFFIX_LP #a2 # * COND_LP #a2 # * ACT_LP #a2 # * ATTRIB_LP #a2 # * PYJSMO_ATTRIB_LP #a2 # * PRINTE_SFORMAT_LP #a2 # end note #a2 # if (match found?) then (yes) #a2 mo, _id = _lp.match(_line) if mo: # if (ID satisfies `--match`) then (yes) #a2 if opt_match_rx and not opt_match_rx.search(_id): continue # Process matching line #a2 :| #lightgreen # endif #a2 # ||:sec:|| Process Matching Line #a3 # start #a3 if _id != cur_id: cur_id = _id # find current diagram #a3 : cur_diagram = diagrams.get(cur_id) # create, if not found #a3 ; if cur_diagram is None: cur_diagram = DIAGRAMS.get(cur_id[0], Diagram)() cur_diagram.id_ = cur_id diagrams[cur_id] = cur_diagram # split line into parts #a3 :| _part_map = _lp.split(_line, mo, cur_diagram) # assemble line parts #a3 :| _line = _lp.assemble(_part_map, cur_diagram) mo = SECTION_RX.search(_line) _start_partition = mo if _start_partition: _line = _line[mo.end(0):] # remove tags from line #a3 : _line = TAG_RX.sub('', _line) _line = _line.rstrip() # escape line (creole does not like backticks) #a3 ; _line = re.sub('`', '//', _line) # if (line starts with ||<U+007C>:sec:|||) then (yes) #a3 if _start_partition: # start partition #a3 :; cur_diagram.open_partition(_line) else: #a3 # append line to diagram #a3 :; cur_diagram.append(_line) # endif #a3 # stop #a3 # first match is sufficient, process next line break #a2 # else (no) #a2 # endif #a2 # endwhile #a2 # endwhile #a2 # endif #a2 output = [] for _id, _diagram in ditems(diagrams): # finish diagrams #a2 :; _diagram.finish() output.extend(( _p.replace('@ld-id@', _id) for _p in ( _diagram.header, '\n'.join(_diagram), _diagram.footer, ) if _p )) output.append('') if opt_file_base: #a2 (yes) # write diagrams to\nseparate files #a2 :; _fh = open(opt_file_base + _id + '.puml', 'w') _fh.write('\n'.join(output)) _fh.close() output = [] # else (no) #a2 # print diagrams on\nstandard output #a2 :; if output: output.pop() if output: printf('\n'.join(output)) # endif #a2 # stop #a2 # (progn (forward-line 1) (snip-insert "py_run.ws_rfid.dispatch_request" t t "python") (insert "\n")) # (progn (forward-line 1) (snip-insert "py_shell.run" t t "python") (insert "\n")) # |:here:| return # result.report() # return max(result.error, ERR_NONE) # -------------------------------------------------- # |||:sec:||| MAIN # -------------------------------------------------- _quiet = False _verbose = False _debug = False # (progn (forward-line 1) (snip-insert "py_f.argparse_callbacks" t t "python") (insert "\n")) _argparse_have_sub_commands = False def _argparse_begin(parser, context=None): # ||:fnc:|| r""" :returns: parser """ if context is None: context = globals() parser.add_argument( '-m', '--match', action='store', type=str, metavar='RX', default=None, help='only extract diagrams matching RX') parser.add_argument( '-b', '--file-base', action='store', type=str, metavar='BASE', default=None, help='Write output to files: `BASE` + `ID` + `INDEX` + ".puml"') parser.add_argument( '--show-bases', action='store_true', dest='show_bases', default=True, help='show base classes for class diagrams (default)') parser.add_argument( '--hide-bases', action='store_false', dest='show_bases', default=True, help='hide base classes for class diagrams') parser.add_argument( '--ignore-bases', action='append', metavar='CLASS', help='ignore base classes for class diagrams') parser.add_argument( '-r', '--replace', action='append', metavar='REP', help='register replacement REP = "SYM VAL"') parser.add_argument( '--verbatim', action='store_true', default=None, help='do not perform standard replacements') # |:here:| return parser def _argparse_end(parser, context=None): # ||:fnc:|| r""" :returns: parser """ if context is None: context = globals() # |:here:| return parser # (progn (forward-line 1) (snip-insert "py_main.py" t t "python" " --key xpy_main_minimal") (insert "\n")) # (progn (forward-line 1) (snip-insert "py.f.setdefaultencoding" t t "py") (insert "\n")) #file_encoding_is_clean = True def setdefaultencoding(encoding=None, quiet=False, context=None): if context is None: context = globals() if context.get('file_encoding_is_clean'): return if encoding is None: encoding='utf-8' try: isinstance('', basestring) if not hasattr(sys, '_setdefaultencoding'): if not quiet: printf('''\ Add this to /etc/python2.x/sitecustomize.py, or put it in local sitecustomize.py and adjust PYTHONPATH=".:${PYTHONPATH}":: try: import sys setattr(sys, '_setdefaultencoding', getattr(sys, 'setdefaultencoding')) except AttributeError: pass Running with reload(sys) hack ... ''', file=sys.stderr) reload(sys) setattr(sys, '_setdefaultencoding', getattr(sys, 'setdefaultencoding')) sys._setdefaultencoding(encoding) except NameError: # python3 already has utf-8 default encoding ;-) pass def main(argv=None, context=None): # ||:fnc:|| if argv is None: argv = sys.argv if context is None: context = globals() context.get( 'setup_logger', globals().get( 'setup_logger', lambda *args: None))(context, True) try: import argparse except ImportError: printe('error: argparse missing. Try `easy_install argparse`.') sys.exit(1) parser = argparse.ArgumentParser(add_help=False) # parser.add_argument('--sum', dest='accumulate', action='store_const', # const=sum, default=max, # help='sum the integers (default: find the max)') # |:opt:| add options context.get('_argparse_begin', lambda _p, *_args: _p)(parser, context) parser.add_argument( '-q', '--quiet', action='store_const', const=-2, dest='debug', default=0, help='suppress warnings') parser.add_argument( '-v', '--verbose', action='store_const', const=-1, dest='debug', default=0, help='verbose test output') parser.add_argument( '-d', '--debug', nargs='?', action='store', type=int, metavar='NUM', default = 0, const = 1, help='show debug information') parser.add_argument( '-t', '--test', action='store_true', help='run doc tests') class AdHocAction(argparse.Action): options = ('implode', 'setup', 'explode', 'extract', 'template', 'eide') def __call__(self, parser, namespace, values, option_string=None): for _opt in self.options: setattr(namespace, 'adhoc_' + _opt, False) setattr(namespace, 'adhoc_' + option_string[2:], True) setattr(namespace, 'adhoc_arg', values) parser.add_argument( '--implode', nargs=0, action=AdHocAction, dest='adhoc_implode', default=False, help='implode script with adhoc') parser.add_argument( '--setup', nargs='?', action=AdHocAction, type=str, metavar='install', dest='adhoc_setup', default=False, const='install', help='explode script into temporary directory and call' ' `python setup.py install`') parser.add_argument( '--explode', nargs='?', action=AdHocAction, type=str, metavar='DIR', dest='adhoc_explode', default=False, const='__adhoc__', help='explode script with adhoc in directory DIR' ' (default: `__adhoc__`)') parser.add_argument( '--extract', nargs='?', action=AdHocAction, type=str, metavar='DIR', dest='adhoc_extract', default=False, const = '.', help='extract files to directory DIR (default: `.`)') parser.add_argument( '--template', nargs='?', action=AdHocAction, type=str, metavar='NAME', dest='adhoc_template', default=False, const = '-', help='extract named template to standard output. default NAME is ``-``') parser.add_argument( '--eide', nargs='?', action=AdHocAction, type=str, metavar='COMM', dest='adhoc_eide', default=False, const = '', help='Emacs IDE template list (implies --template list).') parser.add_argument( '-h', '--help', action='store_true', help="display this help message") parser.add_argument( '--ap-help', action='store_true', help="internal help message") context.get('_argparse_end', lambda _p, *_args: _p)(parser, context) if not context.get('_argparse_have_sub_commands'): # all options and arguments are known # all non-option arguments are consumed by `_parameters.args` parser.add_argument( 'args', nargs='*', metavar='arg', #'args', nargs='+', metavar='arg', #type=argparse.FileType('r'), default=sys.stdin, help='a series of arguments') _parameters = parser.parse_args(argv[1:]) else: # (progn (forward-line 1) (snip-insert "py_f.args_split_range" t t "python") (insert "\n")) def args_split_range(args): next_range = [] for arg in args: next_range.append(arg) if not arg.startswith('-'): break if next_range and not next_range[0].startswith('-'): next_range = [] return next_range, args[len(next_range):] # for sub-commands with their own options: pre-parse to first # non-option argument _parameters = None args = argv[1:] while True: next_range, args = args_split_range(args) if not next_range and not _parameters is None: break _parameters, unknown_args = parser.parse_known_args(next_range, _parameters) if unknown_args: unknown_args.extend(args) args = unknown_args next_range = [] break _parameters.args = args # generate argparse help if _parameters.ap_help: parser.print_help() return 0 # standard help if _parameters.help: help_ = re.sub('\n+[.][.] _END_OF_HELP.*(?s)', '', context['__doc__']) sys.stdout.write(help_ + '\n') return 0 context['_debug'] = _parameters.debug if context['_debug'] > 0: context['_verbose'] = True context['_quiet'] = False elif context['_debug'] < 0: context['_verbose'] = (context['_debug'] == -1) context['_quiet'] = not(context['_verbose']) context['_debug'] = 0 _parameters.debug = context['_debug'] _parameters.verbose = context['_verbose'] _parameters.quiet = context['_quiet'] if context['_debug']: cmd_line = argv sys.stderr.write(sformat( "{0}{3:^{1}} {4:<{2}s}: ]{5!s}[\n", context.get('dbg_comm', '# '), context.get('dbg_twid', 11), context.get('dbg_fwid', 15), ':DBG:', 'cmd_line', cmd_line)) # at least use `quiet` to suppress the setdefaultencoding warning context.get('setdefaultencoding', globals().get('setdefaultencoding', lambda *_a, **_k: None))( quiet=context['_quiet'] or _parameters.test) # |:opt:| handle options # adhoc: implode/setup/explode/extract adhoc_get_opt = lambda opt: getattr( _parameters, 'adhoc_' + opt, None) adhoc_op = sum(((adhoc_get_opt(_opt) and 1) or 0 for _opt in AdHocAction.options)) if adhoc_op: for _opt in AdHocAction.options: setattr(_parameters, _opt, adhoc_get_opt(_opt)) adhoc_export = ( _parameters.setup or _parameters.explode or _parameters.extract) file_ = context['__file__'] source = None have_adhoc = 'AdHoc' in context have_rt_adhoc = 'RtAdHoc' in context # shall adhoc be imported if _parameters.implode or not have_rt_adhoc: # shall this file be compiled adhoc_compile = not (have_rt_adhoc) os_path = os.defpath for pv in ('PATH', 'path'): try: os_path = os.environ[pv] break except KeyError: pass os_path = os_path.split(os.pathsep) for path_dir in os_path: if not path_dir: continue if path_dir not in sys.path: sys.path.append(path_dir) if not have_adhoc: try: import adhoc context['AdHoc'] = adhoc.AdHoc except ImportError: adhoc_compile = False try: from rt_adhoc import RtAdHoc as Adhoc context['AdHoc'] = AdHoc except ImportError: pass else: adhoc_compile = False context['AdHoc'] = context['RtAdHoc'] AdHoc = context['AdHoc'] AdHoc.quiet = context['_quiet'] AdHoc.verbose = context['_verbose'] AdHoc.debug = context['_debug'] AdHoc.include_path.append(os.path.dirname(file_)) AdHoc.extra_templates = [ ] AdHoc.template_process_hooks = { } if _parameters.eide: AdHoc.tt_ide = True AdHoc.tt_comment = _parameters.adhoc_arg or '' AdHoc.tt_prefix = '. (shell-command "' AdHoc.tt_suffix = '")' _parameters.template = True _parameters.adhoc_arg = 'list' if adhoc_compile: ah = AdHoc() source = ah.compileFile(file_) else: file_, source = AdHoc.std_source_param(file_) # implode if _parameters.implode: # @:adhoc_enable:@ # if not context['_quiet']: # map(sys.stderr.write, # ["warning: ", os.path.basename(file_), # " already imploded!\n"]) # @:adhoc_enable:@ AdHoc.write_source('-', source) # explode elif _parameters.setup or _parameters.explode: _here = os.path.abspath('.') _clean_dir = False AdHoc.export_dir = _parameters.adhoc_arg if _parameters.setup: import tempfile _clean_dir = True AdHoc.export_dir = tempfile.mkdtemp('_setup', '__adhoc__') try: AdHoc.export(file_, source) if _parameters.setup: sq = lambda string: ''.join(("'", re.sub("'", """'\\''""", string), "'")) os.chdir(AdHoc.export_dir) os.system(sformat('{0} setup.py {1}', sq(sys.executable), sq(_parameters.adhoc_arg))) finally: if _clean_dir: try: os.chdir(_here) except: pass import shutil shutil.rmtree(AdHoc.export_dir) # extract elif _parameters.extract: AdHoc.extract_dir = _parameters.adhoc_arg AdHoc.extract(file_, source) # template elif _parameters.template: template_name = _parameters.adhoc_arg if not template_name: template_name = '-' if template_name == 'list': sys.stdout.write( '\n'.join(AdHoc.template_table(file_, source)) + '\n') else: template = AdHoc.get_named_template( template_name, file_, source) AdHoc.write_source('-', template) # restore for subsequent calls to main if not have_adhoc: del(AdHoc) return 0 # run doc tests if _parameters.test: import warnings warnings.simplefilter('default') import doctest # for :file:`__init__.py`, :func:`_canonize_module_` does not register the module in `sys.modules`. _canon_name = _module_name = context['__name__'] context.get('_canonize_module_', globals().get( '_canonize_module_', lambda *args: context.__setitem__('__name__', _canon_name)))(context['__name__'], context['__name__'] == '__main__') if context['__name__'] not in sys.modules: sys.modules[context['__name__']] = sys.modules[_module_name] try: logger = logging.getLogger() logger.setLevel(logging.DEBUG) except NameError: pass context.get('_doctest_hook_', lambda *args, **kwargs: None)(context) result = doctest.testmod(sys.modules[context['__name__']], verbose = context['_verbose']) return result.failed # run program final = False ecode = 0 try: try: ecode = context['run'](_parameters) except IOError: (t, e, tb) = sys.exc_info() del(tb) # ignore SIGPIPE import errno if e.errno != errno.EPIPE: raise except SystemExit: raise except: # |:info:| this is used, since module cgitb does not work so well ... (t, e, tb) = sys.exc_info() if not final or context['_debug']: import traceback printf(''.join(traceback.format_tb(tb)), file=sys.stderr, end='') printf(sformat('{0}: {1}', t.__name__, e), file=sys.stderr) del(tb) ecode = 1 return ecode if globals().get('_is_main_', (__name__ == '__main__')): #sys.argv.insert(1, '--debug') # |:debug:| result = main(sys.argv, globals()) sys.exit(result) # |:here:| # (progn (forward-line 1) (snip-insert "py.t.ide" t t "py") (insert "\n")) # # :ide-menu: Emacs IDE Main Menu - Buffer @BUFFER@ # . M-x `eIDE-menu' (eIDE-menu "z") # :ide: CSCOPE ON # . (cscope-minor-mode) # :ide: CSCOPE OFF # . (cscope-minor-mode (quote ( nil ))) # :ide: TAGS: forced update # . (compile (concat "cd /home/ws/project/ws-rfid && make -k FORCED=1 tags")) # :ide: TAGS: update # . (compile (concat "cd /home/ws/project/ws-rfid && make -k tags")) # :ide: +-#+ # . Utilities () # :ide: TOC: Generate TOC with py-toc.py # . (progn (save-buffer) (compile (concat "py-toc.py ./" (file-name-nondirectory (buffer-file-name)) " "))) # :ide: CMD: Fold region with line continuation # . (shell-command-on-region (region-beginning) (region-end) "fold --spaces -width 79 | sed 's, $,,;1!s,^, ,;$!s,$,\\\\,'" nil nil nil t) # :ide: CMD: Fold region and replace with line continuation # . (shell-command-on-region (region-beginning) (region-end) "fold --spaces --width 79 | sed 's, $,,;1!s,^, ,;$!s,$,\\\\,'" t nil nil t) # :ide: +-#+ # . Fold () # :ide: CMD: Remove 8 spaces and add `>>> ' to region # . (shell-command-on-region (region-beginning) (region-end) "sed 's,^ ,,;/^[ ]*##/d;/^[ ]*#/{;s,^ *# *,,p;d;};/^[ ]*$/!s,^,>>> ,'" nil nil nil t) # :ide: CMD: Remove 4 spaces and add `>>> ' to region # . (shell-command-on-region (region-beginning) (region-end) "sed 's,^ ,,;/^[ ]*##/d;/^[ ]*#/{;s,^ *# *,,p;d;};/^[ ]*$/!s,^,>>> ,'" nil nil nil t) # :ide: +-#+ # . Doctest () # :ide: LINT: Check 80 column width ignoring IDE Menus # . (let ((args " | /srv/ftp/pub/check-80-col.sh -")) (compile (concat "sed 's,^\\(\\|. \\|.. \\|... \\)\\(:ide\\|[.] \\).*,,' " (buffer-file-name) " " args " | sed 's,^-," (buffer-file-name) ",'"))) # :ide: LINT: Check 80 column width # . (let ((args "")) (compile (concat "/srv/ftp/pub/check-80-col.sh " (buffer-file-name) " " args))) # :ide: +-#+ # . Lint Tools () # :ide: DELIM: @: SYM :@ @:fillme:@ adhoc tag # . (symbol-tag-normalize-delimiter (cons (cons nil "@:") (cons ":@" nil)) t) # :ide: +-#+ # . Delimiters () # :ide: COMPILE: Run with --ap-help # . (progn (save-buffer) (compile (concat "python ./" (file-name-nondirectory (buffer-file-name)) " --ap-help"))) # :ide: COMPILE: Run with --help # . (progn (save-buffer) (compile (concat "python ./" (file-name-nondirectory (buffer-file-name)) " --help"))) # :ide: COMPILE: Run with --test # . (progn (save-buffer) (compile (concat "python ./" (file-name-nondirectory (buffer-file-name)) " --test"))) # :ide: COMPILE: Run with --test --verbose # . (progn (save-buffer) (compile (concat "python ./" (file-name-nondirectory (buffer-file-name)) " --test --verbose"))) # :ide: COMPILE: Run with --debug # . (progn (save-buffer) (compile (concat "python ./" (file-name-nondirectory (buffer-file-name)) " --debug"))) # :ide: +-#+ # . Compile with standard arguments () # :ide: OCCUR-OUTLINE: Python Source Code # . (x-symbol-tag-occur-outline "sec" '("|||:" ":|||") (cons (cons "^\\([ \t\r]*\\(def\\|class\\)[ ]+\\|[A-Za-z_]?\\)" nil) (cons nil "\\([ \t\r]*(\\|[ \t]*=\\)"))) # :ide: MENU-OUTLINE: Python Source Code # . (x-eIDE-menu-outline "sec" '("|||:" ":|||") (cons (cons "^\\([ \t\r]*\\(def\\|class\\)[ ]+\\|[A-Za-z_]?\\)" nil) (cons nil "\\([ \t\r]*(\\|[ \t]*=\\)"))) # :ide: +-#+ # . Outline () # :ide: INFO: SQLAlchemy - SQL Expression Language - Reference # . (let ((ref-buffer "*sqa-expr-ref*")) (if (not (get-buffer ref-buffer)) (shell-command (concat "w3m -dump -cols " (number-to-string (1- (window-width))) " 'http://www.sqlalchemy.org/docs/05/reference/sqlalchemy/expressions.html'") ref-buffer) (display-buffer ref-buffer t))) # :ide: INFO: SQLAlchemy - SQL Expression Language - Tutorial # . (let ((ref-buffer "*sqa-expr-tutor*")) (if (not (get-buffer ref-buffer)) (shell-command (concat "w3m -dump -cols " (number-to-string (1- (window-width))) " 'http://www.sqlalchemy.org/docs/05/sqlexpression.html'") ref-buffer) (display-buffer ref-buffer t))) # :ide: INFO: SQLAlchemy - Query # . (let ((ref-buffer "*sqa-query*")) (if (not (get-buffer ref-buffer)) (shell-command (concat "w3m -dump -cols " (number-to-string (1- (window-width))) " 'http://www.sqlalchemy.org/docs/orm/query.html'") ref-buffer) (display-buffer ref-buffer t))) # :ide: +-#+ # . SQLAlchemy Reference () # :ide: INFO: Python - argparse # . (let ((ref-buffer "*python-argparse*")) (if (not (get-buffer ref-buffer)) (shell-command (concat "w3m -dump -cols " (number-to-string (1- (window-width))) " 'http://docs.python.org/library/argparse.html'") ref-buffer) (display-buffer ref-buffer t))) # :ide: INFO: Python Documentation # . (let ((ref-buffer "*w3m*")) (if (get-buffer ref-buffer) (display-buffer ref-buffer t)) (other-window 1) (w3m-goto-url "http://docs.python.org/index.html" nil nil)) # :ide: INFO: Python Reference # . (let* ((ref-buffer "*python-ref*") (local "/home/ws/project/ws-util/python/reference/PQR2.7.html") (url (or (and (file-exists-p local) local) "'http://rgruet.free.fr/PQR27/PQR2.7.html'"))) (unless (get-buffer ref-buffer) (get-buffer-create ref-buffer) (with-current-buffer ref-buffer (shell-command (concat "snc txt.py.reference 2>/dev/null") ref-buffer) (goto-char (point-min)) (if (eobp) (shell-command (concat "w3m -dump -cols " (number-to-string (1- (window-width))) " " url) ref-buffer)))) (display-buffer ref-buffer t)) # :ide: +-#+ # . Python Reference () # :ide: COMPILE: Run with --eide # . (progn (save-buffer) (shell-command (concat "python ./" (file-name-nondirectory (buffer-file-name)) " --eide") (concat "*templates: " (file-name-nondirectory (buffer-file-name)) "*"))) # :ide: COMPILE: Run with python3 --test # . (progn (save-buffer) (compile (concat "python3 ./" (file-name-nondirectory (buffer-file-name)) " --test"))) # :ide: COMPILE: Run with python3 w/o args # . (progn (save-buffer) (compile (concat "python3 ./" (file-name-nondirectory (buffer-file-name)) " "))) # :ide: COMPILE: Run with --debug self # . (let ((fn (file-name-nondirectory (buffer-file-name)))) (save-buffer) (compile (concat "python ./" fn " --debug=1 " fn))) # :ide: COMPILE: Run with --match '^a' # . (let ((fn (file-name-nondirectory (buffer-file-name)))) (save-buffer) (compile (concat "python ./" fn " --match '^a' " fn))) # :ide: COMPILE: Run with --match '^c' # . (let ((fn (file-name-nondirectory (buffer-file-name)))) (save-buffer) (compile (concat "python ./" fn " --match '^c' " fn))) # :ide: COMPILE: Run with --file-base line_diversion-check- self # . (let ((fn (file-name-nondirectory (buffer-file-name)))) (save-buffer) (compile (concat "python ./" fn " --file-base line_diversion-check- " fn))) # :ide: COMPILE: Run with self (>.t; diff .e .t) # . (let ((fn (file-name-nondirectory (buffer-file-name)))) (save-buffer) (compile (concat "python ./" fn " " fn " | tee " fn ".t; echo '--------------------'; diff -u " fn ".e " fn ".t"))) # :ide: COMPILE: Run with /home/ws/project/ws_rfid/data/c_wiedenmann/modules/ws_rfid_cust/update_task_nos.py # . (progn (save-buffer) (compile (concat "python ./" (file-name-nondirectory (buffer-file-name)) " /home/ws/project/ws_rfid/data/c_wiedenmann/modules/ws_rfid_cust/update_task_nos.py"))) # :ide: COMPILE: Run with --debug=1 /home/ws/project/ws_rfid/app/ws_rfid_app/models/dba_standard.py # . (progn (save-buffer) (compile (concat "python ./" (file-name-nondirectory (buffer-file-name)) " --debug=1 /home/ws/project/ws_rfid/app/ws_rfid_app/models/dba_standard.py"))) # :ide: COMPILE: Run with --debug=1 /home/ws/snippets/xx.py # . (progn (save-buffer) (compile (concat "python ./" (file-name-nondirectory (buffer-file-name)) " --debug=1 /home/ws/snippets/xx.py"))) # :ide: COMPILE: Run with --test # . (progn (save-buffer) (compile (concat "python ./" (file-name-nondirectory (buffer-file-name)) " --test"))) # :ide: COMPILE: Run with --debug=1 --match '^u0$' /home/ws/project/documentation/bin/sphinx-doc-admin.sh # . (progn (save-buffer) (compile (concat "python ./" (file-name-nondirectory (buffer-file-name)) " --debug=1 --match '^u0$' /home/ws/project/documentation/bin/sphinx-doc-admin.sh"))) # :ide: +-#+ # . Compile () # # Local Variables: # mode: python # comment-start: "#" # comment-start-skip: "#+" # comment-column: 0 # truncate-lines: t # End: