def require_active_token(object):
    """
    Raise an ApiUsageError if the given object is not a token that is currently
    participating in the game.  To be participating in the game, the given
    token must have an id number and be associated with the world.
    """
    require_token(object)
    token = object

    if not token.has_id:
        raise ApiUsageError("""\
                token {token} should have an id, but doesn't.

                This error usually means that a token was added to the world
                without being assigned an id number.  To correct this, make
                sure that you're using a message (i.e. CreateToken) to create
                all of your tokens.""")

    if not token.has_world:
        raise ApiUsageError("""\
                token {token} (id={token.id}) not in world.

                You can get this error if you try to remove the same token from
                the world twice.  This might happen is you don't get rid of
                every reference to a token after it's removed the first time,
                then later on you try to remove the stale reference.""")

def require_active_token ( object ) : require_token ( object ) token = object if not token . has_id : raise ApiUsageError ( ) if not token . has_world : raise ApiUsageError ( )

Raise an ApiUsageError if the given object is not a token that is currently participating in the game . To be participating in the game the given token must have an id number and be associated with the world .


def add_safety_checks(meta, members):
        """
        Iterate through each member of the class being created and add a
        safety check to every method that isn't marked as read-only.
        """
        for member_name, member_value in members.items():
            members[member_name] = meta.add_safety_check(
                    member_name, member_value)

def add_safety_checks ( meta , members ) : for member_name , member_value in members . items ( ) : members [ member_name ] = meta . add_safety_check ( member_name , member_value )

Iterate through each member of the class being created and add a safety check to every method that isn t marked as read - only .


def watch_method(self, method_name, callback):
        """
        Register the given callback to be called whenever the method with the
        given name is called.  You can easily take advantage of this feature in
        token extensions by using the @watch_token decorator.
        """

        # Make sure a token method with the given name exists, and complain if
        # nothing is found.

        try:
            method = getattr(self, method_name)
        except AttributeError:
            raise ApiUsageError("""\
                    {self.__class__.__name__} has no such method
                    {method_name}() to watch.

                    This error usually means that you used the @watch_token
                    decorator on a method of a token extension class that
                    didn't match the name of any method in the corresponding
                    token class.  Check for typos.""")

        # Wrap the method in a WatchedMethod object, if that hasn't already
        # been done.  This object manages a list of callback method and takes
        # responsibility for calling them after the method itself has been
        # called.

        if not isinstance(method, Token.WatchedMethod):
            setattr(self, method_name, Token.WatchedMethod(method))
            method = getattr(self, method_name)

        # Add the given callback to the watched method.

        method.add_watcher(callback)

def watch_method ( self , method_name , callback ) : try : method = getattr ( self , method_name ) except AttributeError : raise ApiUsageError ( ) if not isinstance ( method , Token . WatchedMethod ) : setattr ( self , method_name , Token . WatchedMethod ( method ) ) method = getattr ( self , method_name ) method . add_watcher ( callback )

Register the given callback to be called whenever the method with the given name is called . You can easily take advantage of this feature in token extensions by using the


def _remove_from_world(self):
        """
        Clear all the internal data the token needed while it was part of
        the world.

        Note that this method doesn't actually remove the token from the
        world.  That's what World._remove_token() does.  This method is just
        responsible for setting the internal state of the token being removed.
        """
        self.on_remove_from_world()
        self._extensions = {}
        self._disable_forum_observation()
        self._world = None
        self._id = None

def _remove_from_world ( self ) : self . on_remove_from_world ( ) self . _extensions = { } self . _disable_forum_observation ( ) self . _world = None self . _id = None

Clear all the internal data the token needed while it was part of the world .


def _unlock_temporarily(self):
        """
        Allow tokens to modify the world for the duration of a with-block.

        It's important that tokens only modify the world at appropriate times,
        otherwise the changes they make may not be communicated across the
        network to other clients.  To help catch and prevent these kinds of
        errors, the game engine keeps the world locked most of the time and
        only briefly unlocks it (using this method) when tokens are allowed to
        make changes.  When the world is locked, token methods that aren't
        marked as being read-only can't be called.  When the world is unlocked,
        any token method can be called.  These checks can be disabled by
        running python with optimization enabled.

        You should never call this method manually from within your own game.
        This method is intended to be used by the game engine, which was
        carefully designed to allow the world to be modified only when safe.
        Calling this method yourself disables an important safety check.
        """
        if not self._is_locked:
            yield
        else:
            try:
                self._is_locked = False
                yield
            finally:
                self._is_locked = True

def _unlock_temporarily ( self ) : if not self . _is_locked : yield else : try : self . _is_locked = False yield finally : self . _is_locked = True

Allow tokens to modify the world for the duration of a with - block .


def scan(xml):
    """Converts XML tree to event generator"""

    if xml.tag is et.Comment:
        yield {'type': COMMENT, 'text': xml.text}
        return

    if xml.tag is et.PI:
        if xml.text:
            yield {'type': PI, 'target': xml.target, 'text': xml.text}
        else:
            yield {'type': PI, 'target': xml.target}
        return

    obj = _elt2obj(xml)
    obj['type'] = ENTER
    yield obj

    assert type(xml.tag) is str, xml
    if xml.text:
        yield {'type': TEXT, 'text': xml.text}

    for c in xml:
        for x in scan(c): yield x
        if c.tail:
            yield {'type': TEXT, 'text': c.tail}

    yield {'type': EXIT}

def scan ( xml ) : if xml . tag is et . Comment : yield { 'type' : COMMENT , 'text' : xml . text } return if xml . tag is et . PI : if xml . text : yield { 'type' : PI , 'target' : xml . target , 'text' : xml . text } else : yield { 'type' : PI , 'target' : xml . target } return obj = _elt2obj ( xml ) obj [ 'type' ] = ENTER yield obj assert type ( xml . tag ) is str , xml if xml . text : yield { 'type' : TEXT , 'text' : xml . text } for c in xml : for x in scan ( c ) : yield x if c . tail : yield { 'type' : TEXT , 'text' : c . tail } yield { 'type' : EXIT }


def unscan(events, nsmap=None):
    """Converts events stream into lXML tree"""

    root = None
    last_closed_elt = None
    stack = []
    for obj in events:

        if obj['type'] == ENTER:
            elt = _obj2elt(obj, nsmap=nsmap)
            if stack:
                stack[-1].append(elt)
            elif root is not None:
                raise RuntimeError('Event stream tried to create second XML tree')
            else:
                root = elt
            stack.append(elt)
            last_closed_elt = None

        elif obj['type'] == EXIT:
            last_closed_elt = stack.pop()

        elif obj['type'] == COMMENT:
            elt = et.Comment(obj['text'])
            stack[-1].append(elt)

        elif obj['type'] == PI:
            elt = et.PI(obj['target'])
            if obj.get('text'):
                elt.text = obj['text']
            stack[-1].append(elt)

        elif obj['type'] == TEXT:
            text = obj['text']
            if text:
                if last_closed_elt is None:
                    stack[-1].text = (stack[-1].text or '') + text
                else:
                    last_closed_elt.tail = (last_closed_elt.tail or '') + text
        else:
            assert False, obj

    if root is None:
        raise RuntimeError('Empty XML event stream')

    return root


def parse(filename):
    """Parses file content into events stream"""
    for event, elt in et.iterparse(filename, events= ('start', 'end', 'comment', 'pi'), huge_tree=True):
        if event == 'start':
            obj = _elt2obj(elt)
            obj['type'] = ENTER
            yield obj
            if elt.text:
                yield {'type': TEXT, 'text': elt.text}
        elif event == 'end':
            yield {'type': EXIT}
            if elt.tail:
                yield {'type': TEXT, 'text': elt.tail}
            elt.clear()
        elif event == 'comment':
            yield {'type': COMMENT, 'text': elt.text}
        elif event == 'pi':
            yield {'type': PI, 'text': elt.text}
        else:
            assert False, (event, elt)

def parse ( filename ) : for event , elt in et . iterparse ( filename , events = ( 'start' , 'end' , 'comment' , 'pi' ) , huge_tree = True ) : if event == 'start' : obj = _elt2obj ( elt ) obj [ 'type' ] = ENTER yield obj if elt . text : yield { 'type' : TEXT , 'text' : elt . text } elif event == 'end' : yield { 'type' : EXIT } if elt . tail : yield { 'type' : TEXT , 'text' : elt . tail } elt . clear ( ) elif event == 'comment' : yield { 'type' : COMMENT , 'text' : elt . text } elif event == 'pi' : yield { 'type' : PI , 'text' : elt . text } else : assert False , ( event , elt )


def subtree(events):
    """selects sub-tree events"""
    stack = 0
    for obj in events:
        if obj['type'] == ENTER:
            stack += 1
        elif obj['type'] == EXIT:
            if stack == 0:
                break
            stack -= 1
        yield obj

def subtree ( events ) : stack = 0 for obj in events : if obj [ 'type' ] == ENTER : stack += 1 elif obj [ 'type' ] == EXIT : if stack == 0 : break stack -= 1 yield obj


def merge_text(events):
    """merges each run of successive text events into one text event"""
    text = []
    for obj in events:
        if obj['type'] == TEXT:
            text.append(obj['text'])
        else:
            if text:
                yield {'type': TEXT, 'text': ''.join(text)}
                text.clear()
            yield obj
    if text:
        yield {'type': TEXT, 'text': ''.join(text)}

def merge_text ( events ) : text = [ ] for obj in events : if obj [ 'type' ] == TEXT : text . append ( obj [ 'text' ] ) else : if text : yield { 'type' : TEXT , 'text' : '' . join ( text ) } text . clear ( ) yield obj if text : yield { 'type' : TEXT , 'text' : '' . join ( text ) }


def with_peer(events):
    """locates ENTER peer for each EXIT object. Convenient when selectively
    filtering out XML markup"""

    stack = []
    for obj in events:
        if obj['type'] == ENTER:
            stack.append(obj)
            yield obj, None
        elif obj['type'] == EXIT:
            yield obj, stack.pop()
        else:
            yield obj, None

def with_peer ( events ) : stack = [ ] for obj in events : if obj [ 'type' ] == ENTER : stack . append ( obj ) yield obj , None elif obj [ 'type' ] == EXIT : yield obj , stack . pop ( ) else : yield obj , None

locates ENTER peer for each EXIT object . Convenient when selectively filtering out XML markup


def from_date(datetime_date):
        """
        construct BusinessDate instance from datetime.date instance,
        raise ValueError exception if not possible

        :param datetime.date datetime_date: calendar day
        :return bool:
        """
        return BusinessDate.from_ymd(datetime_date.year, datetime_date.month, datetime_date.day)

def from_date ( datetime_date ) : return BusinessDate . from_ymd ( datetime_date . year , datetime_date . month , datetime_date . day )

construct BusinessDate instance from datetime . date instance raise ValueError exception if not possible


def to_date(self):
        """
        construct datetime.date instance represented calendar date of BusinessDate instance

        :return datetime.date:
        """
        y, m, d = self.to_ymd()
        return date(y, m, d)

def to_date ( self ) : y , m , d = self . to_ymd ( ) return date ( y , m , d )

construct datetime . date instance represented calendar date of BusinessDate instance


def add_period(self, p, holiday_obj=None):
        """
        addition of a period object

        :param BusinessDate d:
        :param p:
        :type p: BusinessPeriod or str
        :param list holiday_obj:
        :return bankdate:
        """

        if isinstance(p, (list, tuple)):
            return [BusinessDate.add_period(self, pd) for pd in p]
        elif isinstance(p, str):
            period = BusinessPeriod(p)
        else:
            period = p

        res = self
        res = BusinessDate.add_months(res, period.months)
        res = BusinessDate.add_years(res, period.years)
        res = BusinessDate.add_days(res, period.days)

        if period.businessdays:
            if holiday_obj:
                res = BusinessDate.add_business_days(res, period.businessdays, holiday_obj)
            else:
                res = BusinessDate.add_business_days(res, period.businessdays, period.holiday)

        return res

def add_period ( self , p , holiday_obj = None ) : if isinstance ( p , ( list , tuple ) ) : return [ BusinessDate . add_period ( self , pd ) for pd in p ] elif isinstance ( p , str ) : period = BusinessPeriod ( p ) else : period = p res = self res = BusinessDate . add_months ( res , period . months ) res = BusinessDate . add_years ( res , period . years ) res = BusinessDate . add_days ( res , period . days ) if period . businessdays : if holiday_obj : res = BusinessDate . add_business_days ( res , period . businessdays , holiday_obj ) else : res = BusinessDate . add_business_days ( res , period . businessdays , period . holiday ) return res


def add_months(self, month_int):
        """
        addition of a number of months

        :param BusinessDate d:
        :param int month_int:
        :return bankdate:
        """

        month_int += self.month
        while month_int > 12:
            self = BusinessDate.add_years(self, 1)
            month_int -= 12
        while month_int < 1:
            self = BusinessDate.add_years(self, -1)
            month_int += 12
        l = monthrange(self.year, month_int)[1]
        return BusinessDate.from_ymd(self.year, month_int, min(l, self.day))

def add_months ( self , month_int ) : month_int += self . month while month_int > 12 : self = BusinessDate . add_years ( self , 1 ) month_int -= 12 while month_int < 1 : self = BusinessDate . add_years ( self , - 1 ) month_int += 12 l = monthrange ( self . year , month_int ) [ 1 ] return BusinessDate . from_ymd ( self . year , month_int , min ( l , self . day ) )


def add_business_days(self, days_int, holiday_obj=None):
        """
        private method for the addition of business days, used in the addition of a BusinessPeriod only

        :param BusinessDate d:
        :param int days_int:
        :param list holiday_obj:
        :return: BusinessDate
        """

        res = self
        if days_int >= 0:
            count = 0
            while count < days_int:
                res = BusinessDate.add_days(res, 1)
                if BusinessDate.is_business_day(res, holiday_obj):
                    count += 1
        else:
            count = 0
            while count > days_int:
                res = BusinessDate.add_days(res, -1)
                if BusinessDate.is_business_day(res, holiday_obj):
                    count -= 1

        return res

def add_business_days ( self , days_int , holiday_obj = None ) : res = self if days_int >= 0 : count = 0 while count < days_int : res = BusinessDate . add_days ( res , 1 ) if BusinessDate . is_business_day ( res , holiday_obj ) : count += 1 else : count = 0 while count > days_int : res = BusinessDate . add_days ( res , - 1 ) if BusinessDate . is_business_day ( res , holiday_obj ) : count -= 1 return res

private method for the addition of business days used in the addition of a BusinessPeriod only


def quoted(parser=any_token):
    """Parses as much as possible until it encounters a matching closing quote.

    By default matches any_token, but can be provided with a more specific parser if required.
    Returns a string
    """
    quote_char = quote()
    value, _ = many_until(parser, partial(one_of, quote_char))
    return build_string(value)

def quoted ( parser = any_token ) : quote_char = quote ( ) value , _ = many_until ( parser , partial ( one_of , quote_char ) ) return build_string ( value )

Parses as much as possible until it encounters a matching closing quote . By default matches any_token but can be provided with a more specific parser if required . Returns a string


def days_in_month(year, month):
    """
    returns number of days for the given year and month

    :param int year: calendar year
    :param int month: calendar month
    :return int:
    """

    eom = _days_per_month[month - 1]
    if is_leap_year(year) and month == 2:
        eom += 1

    return eom

def days_in_month ( year , month ) : eom = _days_per_month [ month - 1 ] if is_leap_year ( year ) and month == 2 : eom += 1 return eom


def setup(self, app):  # noqa
        """Initialize the application."""
        super().setup(app)

        # Setup Database
        self.database.initialize(connect(self.cfg.connection, **self.cfg.connection_params))

        # Fix SQLite in-memory database
        if self.database.database == ':memory:':
            self.cfg.connection_manual = True

        if not self.cfg.migrations_enabled:
            return

        # Setup migration engine
        self.router = Router(self.database, migrate_dir=self.cfg.migrations_path)

        # Register migration commands
        def pw_migrate(name: str=None, fake: bool=False):
            """Run application's migrations.

            :param name: Choose a migration' name
            :param fake: Run as fake. Update migration history and don't touch the database
            """
            self.router.run(name, fake=fake)

        self.app.manage.command(pw_migrate)

        def pw_rollback(name: str=None):
            """Rollback a migration.

            :param name: Migration name (actually it always should be a last one)
            """
            if not name:
                name = self.router.done[-1]
            self.router.rollback(name)

        self.app.manage.command(pw_rollback)

        def pw_create(name: str='auto', auto: bool=False):
            """Create a migration.

            :param name: Set name of migration [auto]
            :param auto: Track changes and setup migrations automatically
            """
            if auto:
                auto = list(self.models.values())
            self.router.create(name, auto)

        self.app.manage.command(pw_create)

        def pw_list():
            """List migrations."""
            self.router.logger.info('Migrations are done:')
            self.router.logger.info('\n'.join(self.router.done))
            self.router.logger.info('')
            self.router.logger.info('Migrations are undone:')
            self.router.logger.info('\n'.join(self.router.diff))

        self.app.manage.command(pw_list)

        @self.app.manage.command
        def pw_merge():
            """Merge migrations into one."""
            self.router.merge()

        self.app.manage.command(pw_merge)

def
setup
(
self
,
app
)
:
super
(
)
.
setup
(
app
)
self
.
database
.
initialize
(
connect
(
self
.
cfg
.
connection
,
**
self
.
cfg
.
connection_params
)
)
if
self
.
database
.
database
==
':memory:'
:
self
.
cfg
.
connection_manual
=
True
if
not
self
.
cfg
.
migrations_enabled
:
return
self
.
router
=
Router
(
self
.
database
,
migrate_dir
=
self
.
cfg
.
migrations_path
)
def
pw_migrate
(
name
:
str
=
,
fake
:
bool
=
False
)
:
self
.
router
.
run
(
name
,
fake
=
fake
)
self
.
app
.
manage
.
command
(
pw_migrate
)
def
pw_rollback
(
name
:
str
=
)
:
if
not
name
:
name
=
self
.
router
.
done
[
1
]
self
.
router
.
rollback
(
name
)
self
.
app
.
manage
.
command
(
pw_rollback
)
def
pw_create
(
name
:
str
=
'auto'
,
auto
:
bool
=
False
)
:
if
auto
:
auto
=
list
(
self
.
models
.
values
(
)
)
self
.
router
.
create
(
name
,
auto
)
self
.
app
.
manage
.
command
(
pw_create
)
def
pw_list
(
)
:
self
.
router
.
logger
.
info
(
'Migrations are done:'
)
self
.
router
.
logger
.
info
(
'\n'
.
join
(
self
.
router
.
done
)
)
self
.
router
.
logger
.
info
(
''
)
self
.
router
.
logger
.
info
(
'Migrations are undone:'
)
self
.
router
.
logger
.
info
(
'\n'
.
join
(
self
.
router
.
diff
)
)
self
.
app
.
manage
.
command
(
pw_list
)
@
self
.
app
.
manage
.
command
def
pw_merge
(
)
:
self
.
router
.
merge
(
)
self
.
app
.
manage
.
command
(
pw_merge
)


def startup(self, app):
        """Register connection's middleware and prepare self database."""
        self.database.init_async(app.loop)
        if not self.cfg.connection_manual:
            app.middlewares.insert(0, self._middleware)

def startup ( self , app ) : self . database . init_async ( app . loop ) if not self . cfg . connection_manual : app . middlewares . insert ( 0 , self . _middleware )

Register connection s middleware and prepare self database .


def cleanup(self, app):
        """Close all connections."""
        if hasattr(self.database.obj, 'close_all'):
            self.database.close_all()

def cleanup ( self , app ) : if hasattr ( self . database . obj , 'close_all' ) : self . database . close_all ( )


def register(self, model):
        """Register a model in self."""
        self.models[model._meta.table_name] = model
        model._meta.database = self.database
        return model

def register ( self , model ) : self . models [ model . _meta . table_name ] = model model . _meta . database = self . database return model


async def manage(self):
        """Manage a database connection."""
        cm = _ContextManager(self.database)
        if isinstance(self.database.obj, AIODatabase):
            cm.connection = await self.database.async_connect()

        else:
            cm.connection = self.database.connect()

        return cm

async def manage ( self ) : cm = _ContextManager ( self . database ) if isinstance ( self . database . obj , AIODatabase ) : cm . connection = await self . database . async_connect ( ) else : cm . connection = self . database . connect ( ) return cm


def migrate(migrator, database, **kwargs):
    """ Write your migrations here.

    > Model = migrator.orm['name']

    > migrator.sql(sql)
    > migrator.create_table(Model)
    > migrator.drop_table(Model, cascade=True)
    > migrator.add_columns(Model, **fields)
    > migrator.change_columns(Model, **fields)
    > migrator.drop_columns(Model, *field_names, cascade=True)
    > migrator.rename_column(Model, old_field_name, new_field_name)
    > migrator.rename_table(Model, new_table_name)
    > migrator.add_index(Model, *col_names, unique=False)
    > migrator.drop_index(Model, index_name)
    > migrator.add_not_null(Model, field_name)
    > migrator.drop_not_null(Model, field_name)
    > migrator.add_default(Model, field_name, default)

    """
    @migrator.create_table
    class DataItem(pw.Model):
        created = pw.DateTimeField(default=dt.datetime.utcnow)
        content = pw.CharField()

def migrate ( migrator , database , ** kwargs ) : @ migrator . create_table class DataItem ( pw . Model ) : created = pw . DateTimeField ( default = dt . datetime . utcnow ) content = pw . CharField ( )


def chain(*args):
    """Runs a series of parsers in sequence passing the result of each parser to the next.
    The result of the last parser is returned.
    """
    def chain_block(*args, **kwargs):
        v = args[0](*args, **kwargs)
        for p in args[1:]:
            v = p(v)
        return v
    return chain_block

def chain ( * args ) : def chain_block ( * args , ** kwargs ) : v = args [ 0 ] ( * args , ** kwargs ) for p in args [ 1 : ] : v = p ( v ) return v return chain_block

Runs a series of parsers in sequence passing the result of each parser to the next . The result of the last parser is returned .


def one_of(these):
    """Returns the current token if is found in the collection provided.

    Fails otherwise.
    """
    ch = peek()
    try:
        if (ch is EndOfFile) or (ch not in these):
            fail(list(these))
    except TypeError:
        if ch != these:
            fail([these])
    next()
    return ch

def one_of ( these ) : ch = peek ( ) try : if ( ch is EndOfFile ) or ( ch not in these ) : fail ( list ( these ) ) except TypeError : if ch != these : fail ( [ these ] ) next ( ) return ch

Returns the current token if is found in the collection provided . Fails otherwise .


def not_one_of(these):
    """Returns the current token if it is not found in the collection provided.

    The negative of one_of.
    """
    ch = peek()
    desc = "not_one_of" + repr(these)
    try:
        if (ch is EndOfFile) or (ch in these):
            fail([desc])
    except TypeError:
        if ch != these:
            fail([desc])
    next()
    return ch

def not_one_of ( these ) : ch = peek ( ) desc = "not_one_of" + repr ( these ) try : if ( ch is EndOfFile ) or ( ch in these ) : fail ( [ desc ] ) except TypeError : if ch != these : fail ( [ desc ] ) next ( ) return ch

Returns the current token if it is not found in the collection provided . The negative of one_of .


def satisfies(guard):
    """Returns the current token if it satisfies the guard function provided.

    Fails otherwise.
    This is the a generalisation of one_of.
    """
    i = peek()
    if (i is EndOfFile) or (not guard(i)):
        fail(["<satisfies predicate " + _fun_to_str(guard) + ">"])
    next()
    return i

def
satisfies
(
guard
)
:
i
=
peek
(
)
if
(
i
is
EndOfFile
)
or
(
not
guard
(
i
)
)
:
fail
(
[
"<satisfies predicate "
+
_fun_to_str
(
guard
)
+
">"
]
)
next
(
)
return
i

Returns the current token if it satisfies the guard function provided . Fails otherwise . This is the a generalisation of one_of .


def not_followed_by(parser):
    """Succeeds if the given parser cannot consume input"""
    @tri
    def not_followed_by_block():
        failed = object()
        result = optional(tri(parser), failed)
        if result != failed:
            fail(["not " + _fun_to_str(parser)])
    choice(not_followed_by_block)

def
not_followed_by
(
parser
)
:
@
tri
def
not_followed_by_block
(
)
:
failed
=
object
(
)
result
=
optional
(
tri
(
parser
)
,
failed
)
if
result
!=
failed
:
fail
(
[
"not "
+
_fun_to_str
(
parser
)
]
)
choice
(
not_followed_by_block
)


def many(parser):
    """Applies the parser to input zero or more times.

    Returns a list of parser results.
    """
    results = []
    terminate = object()
    while local_ps.value:
        result = optional(parser, terminate)
        if result == terminate:
            break
        results.append(result)
    return results

def many ( parser ) : results = [ ] terminate = object ( ) while local_ps . value : result = optional ( parser , terminate ) if result == terminate : break results . append ( result ) return results

Applies the parser to input zero or more times . Returns a list of parser results .


def many_until(these, term):
    """Consumes as many of these as it can until it term is encountered.

    Returns a tuple of the list of these results and the term result
    """
    results = []
    while True:
        stop, result = choice(_tag(True, term),
                              _tag(False, these))
        if stop:
            return results, result
        else:
            results.append(result)

def many_until ( these , term ) : results = [ ] while True : stop , result = choice ( _tag ( True , term ) , _tag ( False , these ) ) if stop : return results , result else : results . append ( result )

Consumes as many of these as it can until it term is encountered . Returns a tuple of the list of these results and the term result


def many_until1(these, term):
    """Like many_until but must consume at least one of these.
    """
    first = [these()]
    these_results, term_result = many_until(these, term)
    return (first + these_results, term_result)

def many_until1 ( these , term ) : first = [ these ( ) ] these_results , term_result = many_until ( these , term ) return ( first + these_results , term_result )

Like many_until but must consume at least one of these .


def sep1(parser, separator):
    """Like sep but must consume at least one of parser.
    """
    first = [parser()]
    def inner():
        separator()
        return parser()
    return first + many(tri(inner))

def sep1 ( parser , separator ) : first = [ parser ( ) ] def inner ( ) : separator ( ) return parser ( ) return first + many ( tri ( inner ) )

Like sep but must consume at least one of parser .


def _fill(self, size):
        """fills the internal buffer from the source iterator"""
        try:
            for i in range(size):
                self.buffer.append(self.source.next())
        except StopIteration:
            self.buffer.append((EndOfFile, EndOfFile))
        self.len = len(self.buffer)

def _fill ( self , size ) : try : for i in range ( size ) : self . buffer . append ( self . source . next ( ) ) except StopIteration : self . buffer . append ( ( EndOfFile , EndOfFile ) ) self . len = len ( self . buffer )


def next(self):
        """Advances to and returns the next token or returns EndOfFile"""
        self.index += 1
        t = self.peek()
        if not self.depth:
            self._cut()
        return t

def next ( self ) : self . index += 1 t = self . peek ( ) if not self . depth : self . _cut ( ) return t


def main(world_cls, referee_cls, gui_cls, gui_actor_cls, ai_actor_cls,
        theater_cls=PygletTheater, default_host=DEFAULT_HOST,
        default_port=DEFAULT_PORT, argv=None):
    """
Run a game being developed with the kxg game engine.

Usage:
    {exe_name} sandbox [<num_ais>] [-v...]
    {exe_name} client [--host HOST] [--port PORT] [-v...]
    {exe_name} server <num_guis> [<num_ais>] [--host HOST] [--port PORT] [-v...]
    {exe_name} debug <num_guis> [<num_ais>] [--host HOST] [--port PORT] [-v...]
    {exe_name} --help

Commands:
    sandbox
        Play a single-player game with the specified number of AIs.  None of
        the multiplayer machinery will be used.

    client
        Launch a client that will try to connect to a server on the given host
        and port.  Once it connects and the game starts, the client will allow
        you to play the game against any other connected clients.

    server
        Launch a server that will manage a game between the given number of
        human and AI players.  The human players must connect using this
        command's client mode.

    debug
        Debug a multiplayer game locally.  This command launches a server and
        the given number of clients all in different processes, and configures
        the logging system such that the output from each process can be easily
        distinguished.

Arguments:
    <num_guis>
        The number of human players that will be playing the game.  Only needed
        by commands that will launch some sort of multiplayer server.

    <num_ais>
        The number of AI players that will be playing the game.  Only needed by
        commands that will launch single-player games or multiplayer servers.

Options:
    -x --host HOST          [default: {default_host}]
        The address of the machine running the server.  Must be accessible from
        the machines running the clients.

    -p --port PORT          [default: {default_port}]
        The port that the server should listen on.  Don't specify a value less
        than 1024 unless the server is running with root permissions.

    -v --verbose
        Have the game engine log more information about what it's doing.  You
        can specify this option several times to get more and more information.

This command is provided so that you can start writing your game with the least
possible amount of boilerplate code.  However, the clients and servers provided
by this command are not capable of running a production game.  Once you have
written your game and want to give it a polished set of menus and options,
you'll have to write new Stage subclasses encapsulating that logic and you'll
have to call those stages yourself by interacting more directly with the
Theater class.  The online documentation has more information on this process.
    """
    import sys, os, docopt, nonstdlib

    exe_name = os.path.basename(sys.argv[0])
    usage = main.__doc__.format(**locals()).strip()
    args = docopt.docopt(usage, argv or sys.argv[1:])
    num_guis = int(args['<num_guis>'] or 1)
    num_ais = int(args['<num_ais>'] or 0)
    host, port = args['--host'], int(args['--port'])

    logging.basicConfig(
            format='%(levelname)s: %(name)s: %(message)s',
            level=nonstdlib.verbosity(args['--verbose']),
    )

    # Use the given game objects and command line arguments to play a game!

    if args['debug']:
        print("""\
****************************** KNOWN BUG WARNING ******************************
In debug mode, every message produced by the logging system gets printed twice.
I know vaguely why this is happening, but as of yet I've not been able to fix
it.  In the mean time, don't let this confuse you!
*******************************************************************************""")
        game = MultiplayerDebugger(
                world_cls, referee_cls, gui_cls, gui_actor_cls, num_guis,
                ai_actor_cls, num_ais, theater_cls, host, port)
    else:
        game = theater_cls()
        ai_actors = [ai_actor_cls() for i in range(num_ais)]

        if args['sandbox']:
            game.gui = gui_cls()
            game.initial_stage = UniplayerGameStage(
                    world_cls(), referee_cls(), gui_actor_cls(), ai_actors)
            game.initial_stage.successor = PostgameSplashStage()

        if args['client']:
            game.gui = gui_cls()
            game.initial_stage = ClientConnectionStage(
                    world_cls(), gui_actor_cls(), host, port)

        if args['server']:
            game.initial_stage = ServerConnectionStage(
                    world_cls(), referee_cls(), num_guis, ai_actors,
                    host, port)

    game.play()

def
main
(
world_cls
,
referee_cls
,
gui_cls
,
gui_actor_cls
,
ai_actor_cls
,
theater_cls
=
PygletTheater
,
default_host
=
DEFAULT_HOST
,
default_port
=
DEFAULT_PORT
,
argv
=
)
:
import
sys
,
os
,
docopt
,
nonstdlib
exe_name
=
os
.
path
.
basename
(
sys
.
argv
[
0
]
)
usage
=
main
.
__doc__
.
format
(
**
locals
(
)
)
.
strip
(
)
args
=
docopt
.
docopt
(
usage
,
argv
or
sys
.
argv
[
1
:
]
)
num_guis
=
int
(
args
[
'<num_guis>'
]
or
1
)
num_ais
=
int
(
args
[
'<num_ais>'
]
or
0
)
host
,
port
=
args
[
'--host'
]
,
int
(
args
[
'--port'
]
)
logging
.
basicConfig
(
format
=
'%(levelname)s: %(name)s: %(message)s'
,
level
=
nonstdlib
.
verbosity
(
args
[
'--verbose'
]
)
,
)
if
args
[
'debug'
]
:
print
(
)
game
=
MultiplayerDebugger
(
world_cls
,
referee_cls
,
gui_cls
,
gui_actor_cls
,
num_guis
,
ai_actor_cls
,
num_ais
,
theater_cls
,
host
,
port
)
else
:
game
=
theater_cls
(
)
ai_actors
=
[
ai_actor_cls
(
)
for
i
in
range
(
num_ais
)
]
if
args
[
'sandbox'
]
:
game
.
gui
=
gui_cls
(
)
game
.
initial_stage
=
UniplayerGameStage
(
world_cls
(
)
,
referee_cls
(
)
,
gui_actor_cls
(
)
,
ai_actors
)
game
.
initial_stage
.
successor
=
PostgameSplashStage
(
)
if
args
[
'client'
]
:
game
.
gui
=
gui_cls
(
)
game
.
initial_stage
=
ClientConnectionStage
(
world_cls
(
)
,
gui_actor_cls
(
)
,
host
,
port
)
if
args
[
'server'
]
:
game
.
initial_stage
=
ServerConnectionStage
(
world_cls
(
)
,
referee_cls
(
)
,
num_guis
,
ai_actors
,
host
,
port
)
game
.
play
(
)

Run a game being developed with the kxg game engine .


def _run_supervisor(self):
        """
        Poll the queues that the worker can use to communicate with the
        supervisor, until all the workers are done and all the queues are
        empty.  Handle messages as they appear.
        """
        import time

        still_supervising = lambda: (
                multiprocessing.active_children()
                or not self.log_queue.empty()
                or not self.exception_queue.empty())

        try:
            while still_supervising():
                # When a log message is received, make a logger with the same
                # name in this process and use it to re-log the message.  It
                # will get handled in this process.

                try:
                    record = self.log_queue.get_nowait()
                    logger = logging.getLogger(record.name)
                    logger.handle(record)
                except queue.Empty:
                    pass

                # When an exception is received, immediately re-raise it.

                try:
                    exception = self.exception_queue.get_nowait()
                except queue.Empty:
                    pass
                else:
                    raise exception

                # Sleep for a little bit, and make sure that the workers haven't
                # outlived their time limit.

                time.sleep(1/self.frame_rate)
                self.elapsed_time += 1/self.frame_rate

                if self.time_limit and self.elapsed_time > self.time_limit:
                    raise RuntimeError("timeout")

        # Make sure the workers don't outlive the supervisor, no matter how the
        # polling loop ended (e.g. normal execution or an exception).

        finally:
            for process in multiprocessing.active_children():
                process.terminate()

def _run_supervisor ( self ) : import time still_supervising = lambda : ( multiprocessing . active_children ( ) or not self . log_queue . empty ( ) or not self . exception_queue . empty ( ) ) try : while still_supervising ( ) : try : record = self . log_queue . get_nowait ( ) logger = logging . getLogger ( record . name ) logger . handle ( record ) except queue . Empty : pass try : exception = self . exception_queue . get_nowait ( ) except queue . Empty : pass else : raise exception time . sleep ( 1 / self . frame_rate ) self . elapsed_time += 1 / self . frame_rate if self . time_limit and self . elapsed_time > self . time_limit : raise RuntimeError ( "timeout" ) finally : for process in multiprocessing . active_children ( ) : process . terminate ( )

Poll the queues that the worker can use to communicate with the supervisor until all the workers are done and all the queues are empty . Handle messages as they appear .


def field_type(self):
        """Return database field type."""
        if not self.model:
            return 'JSON'
        database = self.model._meta.database
        if isinstance(database, Proxy):
            database = database.obj
        if Json and isinstance(database, PostgresqlDatabase):
            return 'JSON'
        return 'TEXT'

def field_type ( self ) : if not self . model : return 'JSON' database = self . model . _meta . database if isinstance ( database , Proxy ) : database = database . obj if Json and isinstance ( database , PostgresqlDatabase ) : return 'JSON' return 'TEXT'


def python_value(self, value):
        """Parse value from database."""
        if self.field_type == 'TEXT' and isinstance(value, str):
            return self.loads(value)
        return value

def python_value ( self , value ) : if self . field_type == 'TEXT' and isinstance ( value , str ) : return self . loads ( value ) return value


def get_fsapi_endpoint(self):
        """Parse the fsapi endpoint from the device url."""
        endpoint = yield from self.__session.get(self.fsapi_device_url, timeout = self.timeout)
        text = yield from endpoint.text(encoding='utf-8')
        doc = objectify.fromstring(text)
        return doc.webfsapi.text

def get_fsapi_endpoint ( self ) : endpoint = yield from self . __session . get ( self . fsapi_device_url , timeout = self . timeout ) text = yield from endpoint . text ( encoding = 'utf-8' ) doc = objectify . fromstring ( text ) return doc . webfsapi . text

Parse the fsapi endpoint from the device url .


def create_session(self):
        """Create a session on the frontier silicon device."""
        req_url = '%s/%s' % (self.__webfsapi, 'CREATE_SESSION')
        sid = yield from self.__session.get(req_url, params=dict(pin=self.pin),
                                            timeout = self.timeout)
        text = yield from sid.text(encoding='utf-8')
        doc = objectify.fromstring(text)
        return doc.sessionId.text

def create_session ( self ) : req_url = '%s/%s' % ( self . __webfsapi , 'CREATE_SESSION' ) sid = yield from self . __session . get ( req_url , params = dict ( pin = self . pin ) , timeout = self . timeout ) text = yield from sid . text ( encoding = 'utf-8' ) doc = objectify . fromstring ( text ) return doc . sessionId . text

Create a session on the frontier silicon device .


def call(self, path, extra=None):
        """Execute a frontier silicon API call."""
        try:
            if not self.__webfsapi:
                self.__webfsapi = yield from self.get_fsapi_endpoint()

            if not self.sid:
                self.sid = yield from self.create_session()

            if not isinstance(extra, dict):
                extra = dict()

            params = dict(pin=self.pin, sid=self.sid)
            params.update(**extra)

            req_url = ('%s/%s' % (self.__webfsapi, path))
            result = yield from self.__session.get(req_url, params=params,
                                                   timeout = self.timeout)
            if result.status == 200:
                text = yield from result.text(encoding='utf-8')
            else:
                self.sid = yield from self.create_session()
                params = dict(pin=self.pin, sid=self.sid)
                params.update(**extra)
                result = yield from self.__session.get(req_url, params=params,
                                                       timeout = self.timeout)
                text = yield from result.text(encoding='utf-8')

            return objectify.fromstring(text)
        except Exception as e:
            logging.info('AFSAPI Exception: ' +traceback.format_exc())

        return None

def
call
(
self
,
path
,
extra
=
)
:
try
:
if
not
self
.
__webfsapi
:
self
.
__webfsapi
=
yield
from
self
.
get_fsapi_endpoint
(
)
if
not
self
.
sid
:
self
.
sid
=
yield
from
self
.
create_session
(
)
if
not
isinstance
(
extra
,
dict
)
:
extra
=
dict
(
)
params
=
dict
(
pin
=
self
.
pin
,
sid
=
self
.
sid
)
params
.
update
(
**
extra
)
req_url
=
(
'%s/%s'
%
(
self
.
__webfsapi
,
path
)
)
result
=
yield
from
self
.
__session
.
get
(
req_url
,
params
=
params
,
timeout
=
self
.
timeout
)
if
result
.
status
==
200
:
text
=
yield
from
result
.
text
(
encoding
=
'utf-8'
)
else
:
self
.
sid
=
yield
from
self
.
create_session
(
)
params
=
dict
(
pin
=
self
.
pin
,
sid
=
self
.
sid
)
params
.
update
(
**
extra
)
result
=
yield
from
self
.
__session
.
get
(
req_url
,
params
=
params
,
timeout
=
self
.
timeout
)
text
=
yield
from
result
.
text
(
encoding
=
'utf-8'
)
return
objectify
.
fromstring
(
text
)
except
Exception
as
e
:
logging
.
info
(
'AFSAPI Exception: '
+
traceback
.
format_exc
(
)
)
return


def handle_set(self, item, value):
        """Helper method for setting a value by using the fsapi API."""
        doc = yield from self.call('SET/{}'.format(item), dict(value=value))
        if doc is None:
            return None

        return doc.status == 'FS_OK'

def handle_set ( self , item , value ) : doc = yield from self . call ( 'SET/{}' . format ( item ) , dict ( value = value ) ) if doc is None : return None return doc . status == 'FS_OK'

Helper method for setting a value by using the fsapi API .


def handle_text(self, item):
        """Helper method for fetching a text value."""
        doc = yield from self.handle_get(item)
        if doc is None:
            return None

        return doc.value.c8_array.text or None

def handle_text ( self , item ) : doc = yield from self . handle_get ( item ) if doc is None : return None return doc . value . c8_array . text or None

Helper method for fetching a text value .


def handle_int(self, item):
        """Helper method for fetching a integer value."""
        doc = yield from self.handle_get(item)
        if doc is None:
            return None

        return int(doc.value.u8.text) or None

def handle_int ( self , item ) : doc = yield from self . handle_get ( item ) if doc is None : return None return int ( doc . value . u8 . text ) or None

Helper method for fetching a integer value .


def handle_long(self, item):
        """Helper method for fetching a long value. Result is integer."""
        doc = yield from self.handle_get(item)
        if doc is None:
            return None

        return int(doc.value.u32.text) or None

def handle_long ( self , item ) : doc = yield from self . handle_get ( item ) if doc is None : return None return int ( doc . value . u32 . text ) or None

Helper method for fetching a long value . Result is integer .


def get_power(self):
        """Check if the device is on."""
        power = (yield from self.handle_int(self.API.get('power')))
        return bool(power)

def get_power ( self ) : power = ( yield from self . handle_int ( self . API . get ( 'power' ) ) ) return bool ( power )


def set_power(self, value=False):
        """Power on or off the device."""
        power = (yield from self.handle_set(
            self.API.get('power'), int(value)))
        return bool(power)

def set_power ( self , value = False ) : power = ( yield from self . handle_set ( self . API . get ( 'power' ) , int ( value ) ) ) return bool ( power )


def get_modes(self):
        """Get the modes supported by this device."""
        if not self.__modes:
            self.__modes = yield from self.handle_list(
                self.API.get('valid_modes'))

        return self.__modes

def get_modes ( self ) : if not self . __modes : self . __modes = yield from self . handle_list ( self . API . get ( 'valid_modes' ) ) return self . __modes

Get the modes supported by this device .


def get_volume_steps(self):
        """Read the maximum volume level of the device."""
        if not self.__volume_steps:
            self.__volume_steps = yield from self.handle_int(
                self.API.get('volume_steps'))

        return self.__volume_steps

def get_volume_steps ( self ) : if not self . __volume_steps : self . __volume_steps = yield from self . handle_int ( self . API . get ( 'volume_steps' ) ) return self . __volume_steps

Read the maximum volume level of the device .


def get_mute(self):
        """Check if the device is muted."""
        mute = (yield from self.handle_int(self.API.get('mute')))
        return bool(mute)

def get_mute ( self ) : mute = ( yield from self . handle_int ( self . API . get ( 'mute' ) ) ) return bool ( mute )


def set_mute(self, value=False):
        """Mute or unmute the device."""
        mute = (yield from self.handle_set(self.API.get('mute'), int(value)))
        return bool(mute)

def set_mute ( self , value = False ) : mute = ( yield from self . handle_set ( self . API . get ( 'mute' ) , int ( value ) ) ) return bool ( mute )


def get_play_status(self):
        """Get the play status of the device."""
        status = yield from self.handle_int(self.API.get('status'))
        return self.PLAY_STATES.get(status)

def get_play_status ( self ) : status = yield from self . handle_int ( self . API . get ( 'status' ) ) return self . PLAY_STATES . get ( status )


def get_equalisers(self):
        """Get the equaliser modes supported by this device."""
        if not self.__equalisers:
            self.__equalisers = yield from self.handle_list(
                self.API.get('equalisers'))

        return self.__equalisers

def get_equalisers ( self ) : if not self . __equalisers : self . __equalisers = yield from self . handle_list ( self . API . get ( 'equalisers' ) ) return self . __equalisers

Get the equaliser modes supported by this device .


def set_sleep(self, value=False):
        """Set device sleep timer."""
        return (yield from self.handle_set(self.API.get('sleep'), int(value)))

def set_sleep ( self , value = False ) : return ( yield from self . handle_set ( self . API . get ( 'sleep' ) , int ( value ) ) )


def _set_range(self, start, stop, value, value_len):
        """
        Assumes that start and stop are already in 'buffer' coordinates. value is a byte iterable.
        value_len is fractional.
        """
        assert stop >= start and value_len >= 0
        range_len = stop - start
        if range_len < value_len:
            self._insert_zeros(stop, stop + value_len - range_len)
            self._copy_to_range(start, value, value_len)
        elif range_len > value_len:
            self._del_range(stop - (range_len - value_len), stop)
            self._copy_to_range(start, value, value_len)
        else:
            self._copy_to_range(start, value, value_len)

def _set_range ( self , start , stop , value , value_len ) : assert stop >= start and value_len >= 0 range_len = stop - start if range_len < value_len : self . _insert_zeros ( stop , stop + value_len - range_len ) self . _copy_to_range ( start , value , value_len ) elif range_len > value_len : self . _del_range ( stop - ( range_len - value_len ) , stop ) self . _copy_to_range ( start , value , value_len ) else : self . _copy_to_range ( start , value , value_len )

Assumes that start and stop are already in buffer coordinates . value is a byte iterable . value_len is fractional .


def _parse_genotype(self, vcf_fields):
        """Parse genotype from VCF line data"""
        format_col = vcf_fields[8].split(':')
        genome_data = vcf_fields[9].split(':')
        try:
            gt_idx = format_col.index('GT')
        except ValueError:
            return []
        return [int(x) for x in re.split(r'[\|/]', genome_data[gt_idx]) if
                x != '.']

def _parse_genotype ( self , vcf_fields ) : format_col = vcf_fields [ 8 ] . split ( ':' ) genome_data = vcf_fields [ 9 ] . split ( ':' ) try : gt_idx = format_col . index ( 'GT' ) except ValueError : return [ ] return [ int ( x ) for x in re . split ( r'[\|/]' , genome_data [ gt_idx ] ) if x != '.' ]


def toIndex(self, value):
		'''
			toIndex - An optional method which will return the value prepped for index.

			By default, "toStorage" will be called. If you provide "hashIndex=True" on the constructor,
			the field will be md5summed for indexing purposes. This is useful for large strings, etc.
		'''
		if self._isIrNull(value):
			ret = IR_NULL_STR
		else:
			ret = self._toIndex(value)

		if self.isIndexHashed is False:
			return ret

		return md5(tobytes(ret)).hexdigest()

def toIndex ( self , value ) : if self . _isIrNull ( value ) : ret = IR_NULL_STR else : ret = self . _toIndex ( value ) if self . isIndexHashed is False : return ret return md5 ( tobytes ( ret ) ) . hexdigest ( )

toIndex - An optional method which will return the value prepped for index .


def copy(self):
		'''
			copy - Create a copy of this IRField.

			  Each subclass should implement this, as you'll need to pass in the args to constructor.

			@return <IRField (or subclass)> - Another IRField that has all the same values as this one.
		'''
		return self.__class__(name=self.name, valueType=self.valueType, defaultValue=self.defaultValue, hashIndex=self.hashIndex)

def copy ( self ) : return self . __class__ ( name = self . name , valueType = self . valueType , defaultValue = self . defaultValue , hashIndex = self . hashIndex )


def objHasUnsavedChanges(self):
		'''
			objHasUnsavedChanges - Check if any object has unsaved changes, cascading.
		'''
		if not self.obj:
			return False

		return self.obj.hasUnsavedChanges(cascadeObjects=True)

def objHasUnsavedChanges ( self ) : if not self . obj : return False return self . obj . hasUnsavedChanges ( cascadeObjects = True )

objHasUnsavedChanges - Check if any object has unsaved changes cascading .


def assert_json_type(value: JsonValue, expected_type: JsonCheckType) -> None:
    """Check that a value has a certain JSON type.

    Raise TypeError if the type does not match.

    Supported types: str, int, float, bool, list, dict, and None.
    float will match any number, int will only match numbers without
    fractional part.

    The special type JList(x) will match a list value where each
    item is of type x:

    >>> assert_json_type([1, 2, 3], JList(int))
    """

    def type_name(t: Union[JsonCheckType, Type[None]]) -> str:
        if t is None:
            return "None"
        if isinstance(t, JList):
            return "list"
        return t.__name__

    if expected_type is None:
        if value is None:
            return
    elif expected_type == float:
        if isinstance(value, float) or isinstance(value, int):
            return
    elif expected_type in [str, int, bool, list, dict]:
        if isinstance(value, expected_type):  # type: ignore
            return
    elif isinstance(expected_type, JList):
        if isinstance(value, list):
            for v in value:
                assert_json_type(v, expected_type.value_type)
            return
    else:
        raise TypeError("unsupported type")
    raise TypeError("wrong JSON type {} != {}".format(
        type_name(expected_type), type_name(type(value))))

def
assert_json_type
(
value
:
JsonValue
,
expected_type
:
JsonCheckType
)
->
:
def
type_name
(
t
:
Union
[
JsonCheckType
,
Type
[
]
]
)
->
str
:
if
t
is
:
return
"None"
if
isinstance
(
t
,
JList
)
:
return
"list"
return
t
.
__name__
if
expected_type
is
:
if
value
is
:
return
elif
expected_type
==
float
:
if
isinstance
(
value
,
float
)
or
isinstance
(
value
,
int
)
:
return
elif
expected_type
in
[
str
,
int
,
bool
,
list
,
dict
]
:
if
isinstance
(
value
,
expected_type
)
:
return
elif
isinstance
(
expected_type
,
JList
)
:
if
isinstance
(
value
,
list
)
:
for
v
in
value
:
assert_json_type
(
v
,
expected_type
.
value_type
)
return
else
:
raise
TypeError
(
"unsupported type"
)
raise
TypeError
(
"wrong JSON type {} != {}"
.
format
(
type_name
(
expected_type
)
,
type_name
(
type
(
value
)
)
)
)

Check that a value has a certain JSON type .


def load(cls, fh):
        """
        Load json or yaml data from file handle.

        Args:
            fh (file): File handle to load from.

        Examlple:
            >>> with open('data.json', 'r') as json:
            >>>    jsdata = composite.load(json)
            >>>
            >>> with open('data.yml', 'r') as yml:
            >>>    ymldata = composite.load(yml)
        """
        dat = fh.read()
        try:
            ret = cls.from_json(dat)
        except:
            ret = cls.from_yaml(dat)
        return ret

def load ( cls , fh ) : dat = fh . read ( ) try : ret = cls . from_json ( dat ) except : ret = cls . from_yaml ( dat ) return ret

Load json or yaml data from file handle .


def from_json(cls, fh):
        """
        Load json from file handle.

        Args:
            fh (file): File handle to load from.

        Examlple:
            >>> with open('data.json', 'r') as json:
            >>>    data = composite.load(json)
        """
        if isinstance(fh, str):
            return cls(json.loads(fh))
        else:
            return cls(json.load(fh))

def from_json ( cls , fh ) : if isinstance ( fh , str ) : return cls ( json . loads ( fh ) ) else : return cls ( json . load ( fh ) )


def intersection(self, other, recursive=True):
        """
        Recursively compute intersection of data. For dictionaries, items
        for specific keys will be reduced to unique items. For lists, items
        will be reduced to unique items. This method is meant to be analogous
        to set.intersection for composite objects.

        Args:
            other (composite): Other composite object to intersect with.
            recursive (bool): Whether or not to perform the operation recursively,
                for all nested composite objects.
        """
        if not isinstance(other, composite):
            raise AssertionError('Cannot intersect composite and {} types'.format(type(other)))

        if self.meta_type != other.meta_type:
            return composite({})

        if self.meta_type == 'list':
            keep = []
            for item in self._list:
                if item in other._list:
                    if recursive and isinstance(item, composite):
                        keep.extend(item.intersection(other.index(item), recursive=True))
                    else:
                        keep.append(item)
            return composite(keep)
        elif self.meta_type == 'dict':
            keep = {}
            for key in self._dict:
                item = self._dict[key]
                if key in other._dict:
                    if recursive and \
                       isinstance(item, composite) and \
                       isinstance(other.get(key), composite):
                       keep[key] = item.intersection(other.get(key), recursive=True)
                    elif item == other[key]:
                        keep[key] = item
            return composite(keep)
        return

Recursively compute intersection of data . For dictionaries items for specific keys will be reduced to unique items . For lists items will be reduced to unique items . This method is meant to be analogous to set . intersection for composite objects .


def union(self, other, recursive=True, overwrite=False):
        """
        Recursively compute union of data. For dictionaries, items
        for specific keys will be combined into a list, depending on the
        status of the overwrite= parameter. For lists, items will be appended
        and reduced to unique items. This method is meant to be analogous
        to set.union for composite objects.

        Args:
            other (composite): Other composite object to union with.
            recursive (bool): Whether or not to perform the operation recursively,
                for all nested composite objects.
            overwrite (bool): Whether or not to overwrite entries with the same
                key in a nested dictionary.
        """
        if not isinstance(other, composite):
            raise AssertionError('Cannot union composite and {} types'.format(type(other)))

        if self.meta_type != other.meta_type:
            return composite([self, other])

        if self.meta_type == 'list':
            keep = []
            for item in self._list:
                keep.append(item)
            for item in other._list:
                if item not in self._list:
                    keep.append(item)
            return composite(keep)
        elif self.meta_type == 'dict':
            keep = {}
            for key in list(set(list(self._dict.keys()) + list(other._dict.keys()))):
                left = self._dict.get(key)
                right = other._dict.get(key)
                if recursive and \
                   isinstance(left, composite) and \
                   isinstance(right, composite):
                    keep[key] = left.union(right, recursive=recursive, overwrite=overwrite)
                elif left == right:
                    keep[key] = left
                elif left is None:
                    keep[key] = right
                elif right is None:
                    keep[key] = left
                elif overwrite:
                    keep[key] = right
                else:
                    keep[key] = composite([left, right])
            return composite(keep)
        return

def
union
(
self
,
other
,
recursive
=
True
,
overwrite
=
False
)
:
if
not
isinstance
(
other
,
composite
)
:
raise
AssertionError
(
'Cannot union composite and {} types'
.
format
(
type
(
other
)
)
)
if
self
.
meta_type
!=
other
.
meta_type
:
return
composite
(
[
self
,
other
]
)
if
self
.
meta_type
==
'list'
:
keep
=
[
]
for
item
in
self
.
_list
:
keep
.
append
(
item
)
for
item
in
other
.
_list
:
if
item
not
in
self
.
_list
:
keep
.
append
(
item
)
return
composite
(
keep
)
elif
self
.
meta_type
==
'dict'
:
keep
=
{
}
for
key
in
list
(
set
(
list
(
self
.
_dict
.
keys
(
)
)
+
list
(
other
.
_dict
.
keys
(
)
)
)
)
:
left
=
self
.
_dict
.
get
(
key
)
right
=
other
.
_dict
.
get
(
key
)
if
recursive
and
isinstance
(
left
,
composite
)
and
isinstance
(
right
,
composite
)
:
keep
[
key
]
=
left
.
union
(
right
,
recursive
=
recursive
,
overwrite
=
overwrite
)
elif
left
==
right
:
keep
[
key
]
=
left
elif
left
is
:
keep
[
key
]
=
right
elif
right
is
:
keep
[
key
]
=
left
elif
overwrite
:
keep
[
key
]
=
right
else
:
keep
[
key
]
=
composite
(
[
left
,
right
]
)
return
composite
(
keep
)
return

Recursively compute union of data . For dictionaries items for specific keys will be combined into a list depending on the status of the overwrite = parameter . For lists items will be appended and reduced to unique items . This method is meant to be analogous to set . union for composite objects .


def append(self, item):
        """
        Append to object, if object is list.
        """
        if self.meta_type == 'dict':
            raise AssertionError('Cannot append to object of `dict` base type!')
        if self.meta_type == 'list':
            self._list.append(item)
        return


def extend(self, item):
        """
        Extend list from object, if object is list.
        """
        if self.meta_type == 'dict':
            raise AssertionError('Cannot extend to object of `dict` base type!')
        if self.meta_type == 'list':
            self._list.extend(item)
        return

Extend list from object if object is list .


def write_json(self, fh, pretty=True):
        """
        Write composite object to file handle in JSON format.

        Args:
            fh (file): File handle to write to.
            pretty (bool): Sort keys and indent in output.
        """
        sjson = json.JSONEncoder().encode(self.json())
        if pretty:
            json.dump(json.loads(sjson), fh, sort_keys=True, indent=4)
        else:
            json.dump(json.loads(sjson), fh)
        return

def write_json ( self , fh , pretty = True ) : sjson = json . JSONEncoder ( ) . encode ( self . json ( ) ) if pretty : json . dump ( json . loads ( sjson ) , fh , sort_keys = True , indent = 4 ) else : json . dump ( json . loads ( sjson ) , fh ) return

Write composite object to file handle in JSON format .


def filelist(self):
        """
        Return list of files in filetree.
        """
        if len(self._filelist) == 0:
            for item in self._data:
                if isinstance(self._data[item], filetree):
                    self._filelist.extend(self._data[item].filelist())
                else:
                    self._filelist.append(self._data[item])
        return self._filelist

def filelist ( self ) : if len ( self . _filelist ) == 0 : for item in self . _data : if isinstance ( self . _data [ item ] , filetree ) : self . _filelist . extend ( self . _data [ item ] . filelist ( ) ) else : self . _filelist . append ( self . _data [ item ] ) return self . _filelist


def prune(self, regex=r".*"):
        """
        Prune leaves of filetree according to specified
        regular expression.

        Args:
            regex (str): Regular expression to use in pruning tree.
        """
        return filetree(self.root, ignore=self.ignore, regex=regex)

def prune ( self , regex = r".*" ) : return filetree ( self . root , ignore = self . ignore , regex = regex )

Prune leaves of filetree according to specified regular expression .


def deref(self, ctx):
        """
        Returns the value this reference is pointing to. This method uses 'ctx' to resolve the reference and return
        the value this reference references.
        If the call was already made, it returns a cached result.
        It also makes sure there's no cyclic reference, and if so raises CyclicReferenceError.
        """
        if self in ctx.call_nodes:
            raise CyclicReferenceError(ctx, self)

        if self in ctx.cached_results:
            return ctx.cached_results[self]

        try:
            ctx.call_nodes.add(self)
            ctx.call_stack.append(self)

            result = self.evaluate(ctx)
            ctx.cached_results[self] = result
            return result
        except:
            if ctx.exception_call_stack is None:
                ctx.exception_call_stack = list(ctx.call_stack)
            raise
        finally:
            ctx.call_stack.pop()
            ctx.call_nodes.remove(self)

def deref ( self , ctx ) : if self in ctx . call_nodes : raise CyclicReferenceError ( ctx , self ) if self in ctx . cached_results : return ctx . cached_results [ self ] try : ctx . call_nodes . add ( self ) ctx . call_stack . append ( self ) result = self . evaluate ( ctx ) ctx . cached_results [ self ] = result return result except : if ctx . exception_call_stack is None : ctx . exception_call_stack = list ( ctx . call_stack ) raise finally : ctx . call_stack . pop ( ) ctx . call_nodes . remove ( self )

Returns the value this reference is pointing to . This method uses ctx to resolve the reference and return the value this reference references . If the call was already made it returns a cached result . It also makes sure there s no cyclic reference and if so raises CyclicReferenceError .


def delete(self):
		'''
			delete - Delete all objects in this list.

			@return <int> - Number of objects deleted
		'''
		if len(self) == 0:
			return 0
		mdl = self.getModel()
		return mdl.deleter.deleteMultiple(self)

def delete ( self ) : if len ( self ) == 0 : return 0 mdl = self . getModel ( ) return mdl . deleter . deleteMultiple ( self )

delete - Delete all objects in this list .


def save(self):
		'''
			save - Save all objects in this list
		'''
		if len(self) == 0:
			return []
		mdl = self.getModel()
		return mdl.saver.save(self)

def save ( self ) : if len ( self ) == 0 : return [ ] mdl = self . getModel ( ) return mdl . saver . save ( self )


def reload(self):
		'''
			reload - Reload all objects in this list.
				Updates in-place. To just fetch all these objects again, use "refetch"

			@return - List (same order as current objects) of either exception (KeyError) if operation failed,
			  or a dict of fields changed -> (old, new)
		'''
		if len(self) == 0:
			return []

		ret = []
		for obj in self:
			res = None
			try:
				res = obj.reload()
			except Exception as e:
				res = e

			ret.append(res)

		return ret

def reload ( self ) : if len ( self ) == 0 : return [ ] ret = [ ] for obj in self : res = None try : res = obj . reload ( ) except Exception as e : res = e ret . append ( res ) return ret

reload - Reload all objects in this list . Updates in - place . To just fetch all these objects again use refetch


def refetch(self):
		'''
			refetch - Fetch a fresh copy of all items in this list.
				Returns a new list. To update in-place, use "reload".

			@return IRQueryableList<IndexedRedisModel> - List of fetched items
		'''

		if len(self) == 0:
			return IRQueryableList()

		mdl = self.getModel()
		pks = [item._id for item in self if item._id]

		return mdl.objects.getMultiple(pks)

def refetch ( self ) : if len ( self ) == 0 : return IRQueryableList ( ) mdl = self . getModel ( ) pks = [ item . _id for item in self if item . _id ] return mdl . objects . getMultiple ( pks )

refetch - Fetch a fresh copy of all items in this list . Returns a new list . To update in - place use reload .


def render(self, *args, **kwargs):
        '''Renders as a str'''
        render_to = StringIO()
        self.output(render_to, *args, **kwargs)
        return render_to.getvalue()

def render ( self , * args , ** kwargs ) : render_to = StringIO ( ) self . output ( render_to , * args , ** kwargs ) return render_to . getvalue ( )


def start_tag(self):
        '''Returns the elements HTML start tag'''
        direct_attributes = (attribute.render(self) for attribute in self.render_attributes)
        attributes = ()
        if hasattr(self, '_attributes'):
            attributes = ('{0}="{1}"'.format(key, value)
                                             for key, value in self.attributes.items() if value)

        rendered_attributes = " ".join(filter(bool, chain(direct_attributes, attributes)))
        return '<{0}{1}{2}{3}>'.format(self.tag, ' ' if rendered_attributes else '',
                                       rendered_attributes, ' /' if self.tag_self_closes else "")

def
start_tag
(
self
)
:
direct_attributes
=
(
attribute
.
render
(
self
)
for
attribute
in
self
.
render_attributes
)
attributes
=
(
)
if
hasattr
(
self
,
'_attributes'
)
:
attributes
=
(
'{0}="{1}"'
.
format
(
key
,
value
)
for
key
,
value
in
self
.
attributes
.
items
(
)
if
value
)
rendered_attributes
=
" "
.
join
(
filter
(
bool
,
chain
(
direct_attributes
,
attributes
)
)
)
return
'<{0}{1}{2}{3}>'
.
format
(
self
.
tag
,
' '
if
rendered_attributes
else
''
,
rendered_attributes
,
' /'
if
self
.
tag_self_closes
else
""
)


def safe_repr(obj):
    """Returns a repr of an object and falls back to a minimal representation of type and ID if the call to repr raised
    an error.

    :param obj: object to safe repr
    :returns: repr string or '(type<id> repr error)' string
    :rtype: str
    """
    try:
        obj_repr = repr(obj)
    except:
        obj_repr = "({0}<{1}> repr error)".format(type(obj), id(obj))
    return obj_repr

def
safe_repr
(
obj
)
:
try
:
obj_repr
=
repr
(
obj
)
except
:
obj_repr
=
"({0}<{1}> repr error)"
.
format
(
type
(
obj
)
,
id
(
obj
)
)
return
obj_repr

Returns a repr of an object and falls back to a minimal representation of type and ID if the call to repr raised an error .


def match_to_clinvar(genome_file, clin_file):
    """
    Match a genome VCF to variants in the ClinVar VCF file

    Acts as a generator, yielding tuples of:
    (ClinVarVCFLine, ClinVarAllele, zygosity)

    'zygosity' is a string and corresponds to the genome's zygosity for that
    ClinVarAllele. It can be either: 'Het' (heterozygous), 'Hom' (homozygous),
    or 'Hem' (hemizygous, e.g. X chromosome in XY individuals).
    """
    clin_curr_line = _next_line(clin_file)
    genome_curr_line = _next_line(genome_file)

    # Ignores all the lines that start with a hashtag
    while clin_curr_line.startswith('#'):
        clin_curr_line = _next_line(clin_file)
    while genome_curr_line.startswith('#'):
        genome_curr_line = _next_line(genome_file)

    # Advance through both files simultaneously to find matches
    while clin_curr_line and genome_curr_line:

        # Advance a file when positions aren't equal.
        clin_curr_pos = VCFLine.get_pos(clin_curr_line)
        genome_curr_pos = VCFLine.get_pos(genome_curr_line)
        try:
            if clin_curr_pos['chrom'] > genome_curr_pos['chrom']:
                genome_curr_line = _next_line(genome_file)
                continue
            elif clin_curr_pos['chrom'] < genome_curr_pos['chrom']:
                clin_curr_line = _next_line(clin_file)
                continue
            if clin_curr_pos['pos'] > genome_curr_pos['pos']:
                genome_curr_line = _next_line(genome_file)
                continue
            elif clin_curr_pos['pos'] < genome_curr_pos['pos']:
                clin_curr_line = _next_line(clin_file)
                continue
        except StopIteration:
            break

        # If we get here, start positions match.
        # Look for allele matching.
        genome_vcf_line = GenomeVCFLine(vcf_line=genome_curr_line,
                                        skip_info=True)
        # We can skip if genome has no allele information for this point.
        if not genome_vcf_line.genotype_allele_indexes:
            genome_curr_line = _next_line(genome_file)
            continue

        # Match only if ClinVar and Genome ref_alleles match.
        clinvar_vcf_line = ClinVarVCFLine(vcf_line=clin_curr_line)
        if not genome_vcf_line.ref_allele == clinvar_vcf_line.ref_allele:
            try:
                genome_curr_line = _next_line(genome_file)
                clin_curr_line = _next_line(clin_file)
                continue
            except StopIteration:
                break

        # Determine genome alleles and zygosity. Zygosity is assumed to be one
        # of: heterozygous, homozygous, or hemizygous.
        genotype_allele_indexes = genome_vcf_line.genotype_allele_indexes
        genome_alleles = [genome_vcf_line.alleles[x] for
                          x in genotype_allele_indexes]
        if len(genome_alleles) == 1:
            zygosity = 'Hem'
        elif len(genome_alleles) == 2:
            if genome_alleles[0].sequence == genome_alleles[1].sequence:
                zygosity = 'Hom'
                genome_alleles = [genome_alleles[0]]
            else:
                zygosity = 'Het'
        else:
            raise ValueError('This code only expects to work on genomes ' +
                             'with one or two alleles called at each ' +
                             'location. The following line violates this:' +
                             str(genome_vcf_line))

        # Look for matches to ClinVar alleles.
        for genome_allele in genome_alleles:
            for allele in clinvar_vcf_line.alleles:
                if genome_allele.sequence == allele.sequence:
                    # The 'records' attribute is specific to ClinVarAlleles.
                    if hasattr(allele, 'records'):
                        yield (genome_vcf_line, allele, zygosity)

        # Done matching, move on.
        try:
            genome_curr_line = _next_line(genome_file)
            clin_curr_line = _next_line(clin_file)
        except StopIteration:
            break


def as_dict(self):
        """Return Allele data as dict object."""
        self_as_dict = dict()
        self_as_dict['sequence'] = self.sequence
        if hasattr(self, 'frequency'):
            self_as_dict['frequency'] = self.frequency
        return self_as_dict

def as_dict ( self ) : self_as_dict = dict ( ) self_as_dict [ 'sequence' ] = self . sequence if hasattr ( self , 'frequency' ) : self_as_dict [ 'frequency' ] = self . frequency return self_as_dict


def _parse_allele_data(self):
        """Create list of Alleles from VCF line data"""
        return [Allele(sequence=x) for x in
                [self.ref_allele] + self.alt_alleles]

def _parse_allele_data ( self ) : return [ Allele ( sequence = x ) for x in [ self . ref_allele ] + self . alt_alleles ]


def _parse_info(self, info_field):
        """Parse the VCF info field"""
        info = dict()
        for item in info_field.split(';'):
            # Info fields may be "foo=bar" or just "foo".
            # For the first case, store key "foo" with value "bar"
            # For the second case, store key "foo" with value True.
            info_item_data = item.split('=')
            # If length is one, just store as a key with value = true.
            if len(info_item_data) == 1:
                info[info_item_data[0]] = True
            elif len(info_item_data) == 2:
                info[info_item_data[0]] = info_item_data[1]
        return info

def _parse_info ( self , info_field ) : info = dict ( ) for item in info_field . split ( ';' ) : info_item_data = item . split ( '=' ) if len ( info_item_data ) == 1 : info [ info_item_data [ 0 ] ] = True elif len ( info_item_data ) == 2 : info [ info_item_data [ 0 ] ] = info_item_data [ 1 ] return info


def as_dict(self):
        """Dict representation of parsed VCF data"""
        self_as_dict = {'chrom': self.chrom,
                        'start': self.start,
                        'ref_allele': self.ref_allele,
                        'alt_alleles': self.alt_alleles,
                        'alleles': [x.as_dict() for x in self.alleles]}
        try:
            self_as_dict['info'] = self.info
        except AttributeError:
            pass
        return self_as_dict

def as_dict ( self ) : self_as_dict = { 'chrom' : self . chrom , 'start' : self . start , 'ref_allele' : self . ref_allele , 'alt_alleles' : self . alt_alleles , 'alleles' : [ x . as_dict ( ) for x in self . alleles ] } try : self_as_dict [ 'info' ] = self . info except AttributeError : pass return self_as_dict


def get_pos(vcf_line):
        """
        Very lightweight parsing of a vcf line to get position.

        Returns a dict containing:
        'chrom': index of chromosome (int), indicates sort order
        'pos': position on chromosome (int)
        """
        if not vcf_line:
            return None
        vcf_data = vcf_line.strip().split('\t')
        return_data = dict()
        return_data['chrom'] = CHROM_INDEX[vcf_data[0]]
        return_data['pos'] = int(vcf_data[1])
        return return_data

def get_pos ( vcf_line ) : if not vcf_line : return None vcf_data = vcf_line . strip ( ) . split ( '\t' ) return_data = dict ( ) return_data [ 'chrom' ] = CHROM_INDEX [ vcf_data [ 0 ] ] return_data [ 'pos' ] = int ( vcf_data [ 1 ] ) return return_data

Very lightweight parsing of a vcf line to get position .


def _toStorage(self, value):
		'''
			_toStorage - Convert the value to a string representation for storage.

			@param value - The value of the item to convert
			@return A string value suitable for storing.
		'''

		for chainedField in self.chainedFields:
			value = chainedField.toStorage(value)

		return value

def _toStorage ( self , value ) : for chainedField in self . chainedFields : value = chainedField . toStorage ( value ) return value

_toStorage - Convert the value to a string representation for storage .


def nav_to_vcf_dir(ftp, build):
    """
    Navigate an open ftplib.FTP to appropriate directory for ClinVar VCF files.

    Args:
        ftp:   (type: ftplib.FTP) an open connection to ftp.ncbi.nlm.nih.gov
        build: (type: string) genome build, either 'b37' or 'b38'
    """
    if build == 'b37':
        ftp.cwd(DIR_CLINVAR_VCF_B37)
    elif build == 'b38':
        ftp.cwd(DIR_CLINVAR_VCF_B38)
    else:
        raise IOError("Genome build not recognized.")

def
nav_to_vcf_dir
(
ftp
,
build
)
:
if
build
==
'b37'
:
ftp
.
cwd
(
DIR_CLINVAR_VCF_B37
)
elif
build
==
'b38'
:
ftp
.
cwd
(
DIR_CLINVAR_VCF_B38
)
else
:
raise
IOError
(
"Genome build not recognized."
)

Navigate an open ftplib . FTP to appropriate directory for ClinVar VCF files .


def as_dict(self, *args, **kwargs):
        """Return ClinVarAllele data as dict object."""
        self_as_dict = super(ClinVarAllele, self).as_dict(*args, **kwargs)
        self_as_dict['hgvs'] = self.hgvs
        self_as_dict['clnalleleid'] = self.clnalleleid
        self_as_dict['clnsig'] = self.clnsig
        self_as_dict['clndn'] = self.clndn
        self_as_dict['clndisdb'] = self.clndisdb
        self_as_dict['clnvi'] = self.clnvi
        return self_as_dict

def as_dict ( self , * args , ** kwargs ) : self_as_dict = super ( ClinVarAllele , self ) . as_dict ( * args , ** kwargs ) self_as_dict [ 'hgvs' ] = self . hgvs self_as_dict [ 'clnalleleid' ] = self . clnalleleid self_as_dict [ 'clnsig' ] = self . clnsig self_as_dict [ 'clndn' ] = self . clndn self_as_dict [ 'clndisdb' ] = self . clndisdb self_as_dict [ 'clnvi' ] = self . clnvi return self_as_dict

Return ClinVarAllele data as dict object .


def _parse_frequencies(self):
        """Parse frequency data in ClinVar VCF"""
        frequencies = OrderedDict([
            ('EXAC', 'Unknown'),
            ('ESP', 'Unknown'),
            ('TGP', 'Unknown')])
        pref_freq = 'Unknown'
        for source in frequencies.keys():
            freq_key = 'AF_' + source
            if freq_key in self.info:
                frequencies[source] = self.info[freq_key]
                if pref_freq == 'Unknown':
                    pref_freq = frequencies[source]
        return pref_freq, frequencies

def _parse_frequencies ( self ) : frequencies = OrderedDict ( [ ( 'EXAC' , 'Unknown' ) , ( 'ESP' , 'Unknown' ) , ( 'TGP' , 'Unknown' ) ] ) pref_freq = 'Unknown' for source in frequencies . keys ( ) : freq_key = 'AF_' + source if freq_key in self . info : frequencies [ source ] = self . info [ freq_key ] if pref_freq == 'Unknown' : pref_freq = frequencies [ source ] return pref_freq , frequencies


def _parse_allele_data(self):
        """Parse alleles for ClinVar VCF, overrides parent method."""

        # Get allele frequencies if they exist.
        pref_freq, frequencies = self._parse_frequencies()

        info_clnvar_single_tags = ['ALLELEID', 'CLNSIG', 'CLNHGVS']
        cln_data = {x.lower(): self.info[x] if x in self.info else None
                    for x in info_clnvar_single_tags}
        cln_data.update(
            {'clndisdb': [x.split(',') for x in
                          self.info['CLNDISDB'].split('|')]
             if 'CLNDISDB' in self.info else []})
        cln_data.update({'clndn': self.info['CLNDN'].split('|') if
                         'CLNDN' in self.info else []})
        cln_data.update({'clnvi': self.info['CLNVI'].split(',')
                        if 'CLNVI' in self.info else []})

        try:
            sequence = self.alt_alleles[0]
        except IndexError:
            sequence = self.ref_allele

        allele = ClinVarAllele(frequency=pref_freq, sequence=sequence,
                               **cln_data)

        # A few ClinVar variants are only reported as a combination with
        # other variants, and no single-variant effect is proposed. Skip these.
        if not cln_data['clnsig']:
            return []

        return [allele]

def _parse_allele_data ( self ) : pref_freq , frequencies = self . _parse_frequencies ( ) info_clnvar_single_tags = [ 'ALLELEID' , 'CLNSIG' , 'CLNHGVS' ] cln_data = { x . lower ( ) : self . info [ x ] if x in self . info else None for x in info_clnvar_single_tags } cln_data . update ( { 'clndisdb' : [ x . split ( ',' ) for x in self . info [ 'CLNDISDB' ] . split ( '|' ) ] if 'CLNDISDB' in self . info else [ ] } ) cln_data . update ( { 'clndn' : self . info [ 'CLNDN' ] . split ( '|' ) if 'CLNDN' in self . info else [ ] } ) cln_data . update ( { 'clnvi' : self . info [ 'CLNVI' ] . split ( ',' ) if 'CLNVI' in self . info else [ ] } ) try : sequence = self . alt_alleles [ 0 ] except IndexError : sequence = self . ref_allele allele = ClinVarAllele ( frequency = pref_freq , sequence = sequence , ** cln_data ) if not cln_data [ 'clnsig' ] : return [ ] return [ allele ]

Parse alleles for ClinVar VCF overrides parent method .


def add(self, *names):
        '''Returns back a class decorator that enables registering Blox to this factory'''
        def decorator(blok):
            for name in names or (blok.__name__, ):
                self[name] = blok
            return blok
        return decorator

def add ( self , * names ) : def decorator ( blok ) : for name in names or ( blok . __name__ , ) : self [ name ] = blok return blok return decorator


def depricated_name(newmethod):
    """
    Decorator for warning user of depricated functions before use.

    Args:
        newmethod (str): Name of method to use instead.
    """
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            warnings.simplefilter('always', DeprecationWarning)
            warnings.warn(
                "Function {} is depricated, please use {} instead.".format(func.__name__, newmethod),
                category=DeprecationWarning, stacklevel=2
            )
            warnings.simplefilter('default', DeprecationWarning)
            return func(*args, **kwargs)
        return wrapper
    return decorator

Decorator for warning user of depricated functions before use .


def setDefaultRedisConnectionParams( connectionParams ):
	'''
		setDefaultRedisConnectionParams - Sets the default parameters used when connecting to Redis.

		  This should be the args to redis.Redis in dict (kwargs) form.

		  @param connectionParams <dict> - A dict of connection parameters.
		    Common keys are:

		       host <str> - hostname/ip of Redis server (default '127.0.0.1')
		       port <int> - Port number			(default 6379)
		       db  <int>  - Redis DB number		(default 0)

		   Omitting any of those keys will ensure the default value listed is used.

		  This connection info will be used by default for all connections to Redis, unless explicitly set otherwise.
		  The common way to override is to define REDIS_CONNECTION_PARAMS on a model, or use AltConnectedModel = MyModel.connectAlt( PARAMS )

		  Any omitted fields in these connection overrides will inherit the value from the global default.

		  For example, if your global default connection params define host = 'example.com', port=15000, and db=0,
		    and then one of your models has

		       REDIS_CONNECTION_PARAMS = { 'db' : 1 }

		    as an attribute, then that model's connection will inherit host='example.com" and port=15000 but override db and use db=1

		    NOTE: Calling this function will clear the connection_pool attribute of all stored managed connections, disconnect all managed connections,
		      and close-out the connection pool.
		     It may not be safe to call this function while other threads are potentially hitting Redis (not that it would make sense anyway...)

		     @see clearRedisPools   for more info
	'''
	global _defaultRedisConnectionParams
	_defaultRedisConnectionParams.clear()

	for key, value in connectionParams.items():
		_defaultRedisConnectionParams[key] = value

	clearRedisPools()

def setDefaultRedisConnectionParams ( connectionParams ) : global _defaultRedisConnectionParams _defaultRedisConnectionParams . clear ( ) for key , value in connectionParams . items ( ) : _defaultRedisConnectionParams [ key ] = value clearRedisPools ( )

setDefaultRedisConnectionParams - Sets the default parameters used when connecting to Redis .


def clearRedisPools():
	'''
		clearRedisPools - Disconnect all managed connection pools,
		   and clear the connectiobn_pool attribute on all stored managed connection pools.

		   A "managed" connection pool is one where REDIS_CONNECTION_PARAMS does not define the "connection_pool" attribute.
		   If you define your own pools, IndexedRedis will use them and leave them alone.

		  This method will be called automatically after calling setDefaultRedisConnectionParams.

		  Otherwise, you shouldn't have to call it.. Maybe as some sort of disaster-recovery call..
	'''
	global RedisPools
	global _redisManagedConnectionParams

	for pool in RedisPools.values():
		try:
			pool.disconnect()
		except:
			pass

	for paramsList in _redisManagedConnectionParams.values():
		for params in paramsList:
			if 'connection_pool' in params:
				del params['connection_pool']

	RedisPools.clear()
	_redisManagedConnectionParams.clear()

def clearRedisPools ( ) : global RedisPools global _redisManagedConnectionParams for pool in RedisPools . values ( ) : try : pool . disconnect ( ) except : pass for paramsList in _redisManagedConnectionParams . values ( ) : for params in paramsList : if 'connection_pool' in params : del params [ 'connection_pool' ] RedisPools . clear ( ) _redisManagedConnectionParams . clear ( )

clearRedisPools - Disconnect all managed connection pools and clear the connectiobn_pool attribute on all stored managed connection pools .


def getRedisPool(params):
	'''
		getRedisPool - Returns and possibly also creates a Redis connection pool
			based on the REDIS_CONNECTION_PARAMS passed in.

			The goal of this method is to keep a small connection pool rolling
			to each unique Redis instance, otherwise during network issues etc
			python-redis will leak connections and in short-order can exhaust
			all the ports on a system. There's probably also some minor
			performance gain in sharing Pools.

			Will modify "params", if "host" and/or "port" are missing, will fill
			them in with defaults, and prior to return will set "connection_pool"
			on params, which will allow immediate return on the next call,
			and allow access to the pool directly from the model object.

			@param params <dict> - REDIS_CONNECTION_PARAMS - kwargs to redis.Redis

			@return redis.ConnectionPool corrosponding to this unique server.
	'''
	global RedisPools
	global _defaultRedisConnectionParams
	global _redisManagedConnectionParams

	if not params:
		params = _defaultRedisConnectionParams
		isDefaultParams = True
	else:
		isDefaultParams = bool(params is _defaultRedisConnectionParams)

	if 'connection_pool' in params:
		return params['connection_pool']

	hashValue = hashDictOneLevel(params)

	if hashValue in RedisPools:
		params['connection_pool'] = RedisPools[hashValue]
		return RedisPools[hashValue]

	# Copy the params, so that we don't modify the original dict
	if not isDefaultParams:
		origParams = params
		params = copy.copy(params)
	else:
		origParams = params

	checkAgain = False
	if 'host' not in params:
		if not isDefaultParams and 'host' in _defaultRedisConnectionParams:
			params['host'] = _defaultRedisConnectionParams['host']
		else:
			params['host'] = '127.0.0.1'
		checkAgain = True
	if 'port' not in params:
		if not isDefaultParams and 'port' in _defaultRedisConnectionParams:
			params['port'] = _defaultRedisConnectionParams['port']
		else:
			params['port'] = 6379
		checkAgain = True

	if 'db' not in params:
		if not isDefaultParams and 'db' in _defaultRedisConnectionParams:
			params['db'] = _defaultRedisConnectionParams['db']
		else:
			params['db'] = 0
		checkAgain = True

	if not isDefaultParams:
		otherGlobalKeys = set(_defaultRedisConnectionParams.keys()) - set(params.keys())
		for otherKey in otherGlobalKeys:
			if otherKey == 'connection_pool':
				continue
			params[otherKey] = _defaultRedisConnectionParams[otherKey]
			checkAgain = True

	if checkAgain:
		hashValue = hashDictOneLevel(params)
		if hashValue in RedisPools:
			params['connection_pool'] = RedisPools[hashValue]
			return RedisPools[hashValue]

	connectionPool = redis.ConnectionPool(**params)
	origParams['connection_pool'] = params['connection_pool'] = connectionPool
	RedisPools[hashValue] = connectionPool

	# Add the original as a "managed" redis connection (they did not provide their own pool)
	#   such that if the defaults change, we make sure to re-inherit any keys, and can disconnect
	#   from clearRedisPools
	origParamsHash = hashDictOneLevel(origParams)
	if origParamsHash not in _redisManagedConnectionParams:
		_redisManagedConnectionParams[origParamsHash] = [origParams]
	elif origParams not in _redisManagedConnectionParams[origParamsHash]:
		_redisManagedConnectionParams[origParamsHash].append(origParams)

	return connectionPool

def getRedisPool ( params ) : global RedisPools global _defaultRedisConnectionParams global _redisManagedConnectionParams if not params : params = _defaultRedisConnectionParams isDefaultParams = True else : isDefaultParams = bool ( params is _defaultRedisConnectionParams ) if 'connection_pool' in params : return params [ 'connection_pool' ] hashValue = hashDictOneLevel ( params ) if hashValue in RedisPools : params [ 'connection_pool' ] = RedisPools [ hashValue ] return RedisPools [ hashValue ] if not isDefaultParams : origParams = params params = copy . copy ( params ) else : origParams = params checkAgain = False if 'host' not in params : if not isDefaultParams and 'host' in _defaultRedisConnectionParams : params [ 'host' ] = _defaultRedisConnectionParams [ 'host' ] else : params [ 'host' ] = '127.0.0.1' checkAgain = True if 'port' not in params : if not isDefaultParams and 'port' in _defaultRedisConnectionParams : params [ 'port' ] = _defaultRedisConnectionParams [ 'port' ] else : params [ 'port' ] = 6379 checkAgain = True if 'db' not in params : if not isDefaultParams and 'db' in _defaultRedisConnectionParams : params [ 'db' ] = _defaultRedisConnectionParams [ 'db' ] else : params [ 'db' ] = 0 checkAgain = True if not isDefaultParams : otherGlobalKeys = set ( _defaultRedisConnectionParams . keys ( ) ) - set ( params . keys ( ) ) for otherKey in otherGlobalKeys : if otherKey == 'connection_pool' : continue params [ otherKey ] = _defaultRedisConnectionParams [ otherKey ] checkAgain = True if checkAgain : hashValue = hashDictOneLevel ( params ) if hashValue in RedisPools : params [ 'connection_pool' ] = RedisPools [ hashValue ] return RedisPools [ hashValue ] connectionPool = redis . ConnectionPool ( ** params ) origParams [ 'connection_pool' ] = params [ 'connection_pool' ] = connectionPool RedisPools [ hashValue ] = connectionPool origParamsHash = hashDictOneLevel ( origParams ) if origParamsHash not in _redisManagedConnectionParams : _redisManagedConnectionParams [ origParamsHash ] = [ origParams ] elif origParams not in _redisManagedConnectionParams [ origParamsHash ] : _redisManagedConnectionParams [ origParamsHash ] . append ( origParams ) return connectionPool

getRedisPool - Returns and possibly also creates a Redis connection pool based on the REDIS_CONNECTION_PARAMS passed in .


def pprint(self, stream=None):
		'''
			pprint - Pretty-print a dict representation of this object.

			@param stream <file/None> - Either a stream to output, or None to default to sys.stdout
		'''
		pprint.pprint(self.asDict(includeMeta=True, forStorage=False, strKeys=True), stream=stream)

def pprint ( self , stream = None ) : pprint . pprint ( self . asDict ( includeMeta = True , forStorage = False , strKeys = True ) , stream = stream )

pprint - Pretty - print a dict representation of this object .


def hasUnsavedChanges(self, cascadeObjects=False):
		'''
			hasUnsavedChanges - Check if any unsaved changes are present in this model, or if it has never been saved.

			@param cascadeObjects <bool> default False, if True will check if any foreign linked objects themselves have unsaved changes (recursively).
				Otherwise, will just check if the pk has changed.

			@return <bool> - True if any fields have changed since last fetch, or if never saved. Otherwise, False
		'''
		if not self._id or not self._origData:
			return True

		for thisField in self.FIELDS:
			thisVal = object.__getattribute__(self, thisField)
			if self._origData.get(thisField, '') != thisVal:
				return True

			if cascadeObjects is True and issubclass(thisField.__class__, IRForeignLinkFieldBase):
				if thisVal.objHasUnsavedChanges():
					return True

		return False

def hasUnsavedChanges ( self , cascadeObjects = False ) : if not self . _id or not self . _origData : return True for thisField in self . FIELDS : thisVal = object . __getattribute__ ( self , thisField ) if self . _origData . get ( thisField , '' ) != thisVal : return True if cascadeObjects is True and issubclass ( thisField . __class__ , IRForeignLinkFieldBase ) : if thisVal . objHasUnsavedChanges ( ) : return True return False

hasUnsavedChanges - Check if any unsaved changes are present in this model or if it has never been saved .


def diff(firstObj, otherObj, includeMeta=False):
		'''
			diff - Compare the field values on two IndexedRedisModels.

			@param firstObj <IndexedRedisModel instance> - First object (or self)

			@param otherObj <IndexedRedisModel instance> - Second object

			@param includeMeta <bool> - If meta information (like pk) should be in the diff results.

			@return <dict> - Dict of  'field' : ( value_firstObjForField, value_otherObjForField ).

				Keys are names of fields with different values.
				Value is a tuple of ( value_firstObjForField, value_otherObjForField )

			Can be called statically, like: IndexedRedisModel.diff ( obj1, obj2 )

			  or in reference to an obj   : obj1.diff(obj2)
		'''

		if not isIndexedRedisModel(firstObj):
			raise ValueError('Type < %s > does not extend IndexedRedisModel.' %( type(firstObj).__name__ , ) )
		if not isIndexedRedisModel(otherObj):
			raise ValueError('Type < %s > does not extend IndexedRedisModel.' %( type(otherObj).__name__ , ) )

		firstObj.validateModel()
		otherObj.validateModel()

		# Types may not match, but could be subclass, special copy class (like connectAlt), etc.
		#   So check if FIELDS matches, and if so, we can continue.
		if getattr(firstObj, 'FIELDS') != getattr(otherObj, 'FIELDS'):
			# NOTE: Maybe we should iterate here and compare just that field types and names match?
			#   In case a copy changes a default or something, we would still be able to diff..
			raise ValueError('Cannot compare  < %s > and < %s > . Must be same model OR have equal FIELDS.' %( firstObj.__class__, otherObj.__class__) )

		diffFields = {}

		for thisField in firstObj.FIELDS:

			thisFieldStr = str(thisField)

			firstVal = object.__getattribute__( firstObj, thisFieldStr )
			otherVal = object.__getattribute__( otherObj, thisFieldStr )

			if firstVal != otherVal:
				diffFields[ thisFieldStr ] = ( (firstVal, otherVal) )

		if includeMeta:
			firstPk = firstObj.getPk()
			otherPk = otherObj.getPk()
			if firstPk != otherPk:
				diffFields['_id'] = ( firstPk, otherPk )

		return diffFields

diff - Compare the field values on two IndexedRedisModels .


def save(self, cascadeSave=True):
		'''
			save - Save this object.

			Will perform an "insert" if this object had not been saved before,
			  otherwise will update JUST the fields changed on THIS INSTANCE of the model.

			  i.e. If you have two processes fetch the same object and change different fields, they will not overwrite
			  eachother, but only save the ones each process changed.

			If you want to save multiple objects of type MyModel in a single transaction,
			and you have those objects in a list, myObjs, you can do the following:

				MyModel.saver.save(myObjs)

			@param cascadeSave <bool> Default True - If True, any Foreign models linked as attributes that have been altered
			   or created will be saved with this object. If False, only this object (and the reference to an already-saved foreign model) will be saved.

			@see #IndexedRedisSave.save

			@return <list> - Single element list, id of saved object (if successful)
		'''
		saver = IndexedRedisSave(self.__class__)
		return saver.save(self, cascadeSave=cascadeSave)

def save ( self , cascadeSave = True ) : saver = IndexedRedisSave ( self . __class__ ) return saver . save ( self , cascadeSave = cascadeSave )

save - Save this object . Will perform an insert if this object had not been saved before otherwise will update JUST the fields changed on THIS INSTANCE of the model .