Skip to content

Commit

Permalink
0.21.2: integrated sly and doc tweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
mynl committed Nov 23, 2023
1 parent a06c2fc commit 6472899
Show file tree
Hide file tree
Showing 11 changed files with 2,922 additions and 12 deletions.
21 changes: 21 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,27 @@ Installation
Version History
-----------------

0.21.2
~~~~~~~~

* Misc documentation updates.
* Experimental magic functions, allowing, eg. %agg [spec] to create an aggregate object (one-liner).
* 0.21.1 yanked from pypi due to error in pyproject.toml.

0.21.0
~~~~~~~~~

* Moved ``sly`` into the project for better control. ``sly`` is a Python implementation of lex and yacc parsing tools.
It is written by Dave Beazley. Per the sly repo on github:

The SLY project is no longer making package-installable releases. It's fully functional, but if choose to use it,
you should vendor the code into your application. SLY has zero-dependencies. Although I am semi-retiring the project,
I will respond to bug reports and still may decide to make future changes to it depending on my mood.
I'd like to thank everyone who has contributed to it over the years. --Dave

* Experimenting with a line/cell DecL magic interpreter in Jupyter Lab to obviate the
need for ``build``.

0.20.2
~~~~~~~~~

Expand Down
4 changes: 3 additions & 1 deletion aggregate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
from .constants import *
from .random_agg import *
from .decl_pygments import *
from .sly import *
from .agg_magics import *

import sys

Expand All @@ -47,7 +49,7 @@
__email__ = "steve@convexrisk.com"
__status__ = "beta"
# only need to change here, feeds conf.py (docs) and pyproject.toml (build)
__version__ = "0.20.2"
__version__ = "0.21.2"



Expand Down
73 changes: 73 additions & 0 deletions aggregate/agg_magics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from IPython.core.magic import magics_class, Magics
from IPython.core.magic import cell_magic, line_magic
from IPython.core.magic_arguments import magic_arguments, argument, parse_argstring

@magics_class
class AggregateMagic(Magics):
"""
description: implements magics to help using Aggregate class
"""

# @magic_arguments()
# @argument('-d', '--debug', action='store_false', help='Debug mode; show commands.')
@line_magic
def agg(self, line):
"""
Implements the agg magic. Allows::
%agg ObName 1 claim dsev [1:6] poisson
which is exapneded to::
ObName = build("agg ObName 1 claim dsev [1:6] poisson")
"""

# args = parse_argstring(self.agg, line)
# print(args)

debug = False
if line.find('-d') >= 0:
debug = True
line = line.replace('-d', '')

nm = line.strip().split(' ')[0]
cmd0 = f'{nm} = build("agg {line}")'
cmd1 = f'qd({nm})'
if debug:
print(cmd0)
print(cmd1)
self.shell.ex(cmd0)
self.shell.ev(cmd1)

@magic_arguments()
@argument('--log2', type=int, default=16, help='Specify log2 to determine the number of buckets.')
@argument('--bs', type=str, default='', help='Set bucket size, converted to float.')
@argument('-u', '--update', action='store_false', help='Suppress update.')
@argument('-n', '--normalize', action='store_false', help='Do not normalize severity.')
@cell_magic
def decl(self, line, cell=None):
"""
WIP NYI for functional.
"""
args = parse_argstring(self.decl, line)

print(args)
bs = 0.
if args.bs != '':
try:
bs = eval(args.bs)
except: # noqa
pass

cmd0 = (f'a = build("""{cell}""", update={args.update}, normalize={args.normalize},'
f'log2={args.log2}, bs={bs})')
self.shell.ex(f'cmd0')
self.shell.ex(f'cmd0')


def load_ipython_extension(ipython):
ipython.register_magics(AggregateMagic)
3 changes: 1 addition & 2 deletions aggregate/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@
from numpy import exp
from pathlib import Path
import re
from sly import Lexer, Parser
import sly
from . sly import Lexer, Parser

logger = logging.getLogger(__name__)

Expand Down
6 changes: 6 additions & 0 deletions aggregate/sly/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

from .lex import *
from .yacc import *

__version__ = "0.5"
__all__ = [ *lex.__all__, *yacc.__all__ ]
25 changes: 25 additions & 0 deletions aggregate/sly/ast.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# sly/ast.py
import sys

class AST(object):

@classmethod
def __init_subclass__(cls, **kwargs):
mod = sys.modules[cls.__module__]
if not hasattr(cls, '__annotations__'):
return

hints = list(cls.__annotations__.items())

def __init__(self, *args, **kwargs):
if len(hints) != len(args):
raise TypeError(f'Expected {len(hints)} arguments')
for arg, (name, val) in zip(args, hints):
if isinstance(val, str):
val = getattr(mod, val)
if not isinstance(arg, val):
raise TypeError(f'{name} argument must be {val}')
setattr(self, name, arg)

cls.__init__ = __init__

60 changes: 60 additions & 0 deletions aggregate/sly/docparse.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# docparse.py
#
# Support doc-string parsing classes

__all__ = [ 'DocParseMeta' ]

class DocParseMeta(type):
'''
Metaclass that processes the class docstring through a parser and
incorporates the result into the resulting class definition. This
allows Python classes to be defined with alternative syntax.
To use this class, you first need to define a lexer and parser:
from sly import Lexer, Parser
class MyLexer(Lexer):
...
class MyParser(Parser):
...
You then need to define a metaclass that inherits from DocParseMeta.
This class must specify the associated lexer and parser classes.
For example:
class MyDocParseMeta(DocParseMeta):
lexer = MyLexer
parser = MyParser
This metaclass is then used as a base for processing user-defined
classes:
class Base(metaclass=MyDocParseMeta):
pass
class Spam(Base):
"""
doc string is parsed
...
"""
It is expected that the MyParser() class would return a dictionary.
This dictionary is used to create the final class Spam in this example.
'''

@staticmethod
def __new__(meta, clsname, bases, clsdict):
if '__doc__' in clsdict:
lexer = meta.lexer()
parser = meta.parser()
lexer.cls_name = parser.cls_name = clsname
lexer.cls_qualname = parser.cls_qualname = clsdict['__qualname__']
lexer.cls_module = parser.cls_module = clsdict['__module__']
parsedict = parser.parse(lexer.tokenize(clsdict['__doc__']))
assert isinstance(parsedict, dict), 'Parser must return a dictionary'
clsdict.update(parsedict)
return super().__new__(meta, clsname, bases, clsdict)

@classmethod
def __init_subclass__(cls):
assert hasattr(cls, 'parser') and hasattr(cls, 'lexer')
Loading

0 comments on commit 6472899

Please sign in to comment.