PK!ᡘggstories/__init__.py""" stories ------- This module implements Business Transaction DSL. :copyright: (c) 2018-2019 dry-python team. :license: BSD, see LICENSE for more details. """ from ._argument import arguments from ._return import Failure, Result, Skip, Success from ._story import Story as story __all__ = ["story", "arguments", "Result", "Success", "Failure", "Skip"] PK!stories/_argument.pydef arguments(*names): def decorator(f): f.arguments = list(names) return f return decorator def get_arguments(f): return getattr(f, "arguments", []) PK!/@stories/_collect.pydef collect_story(f): calls = [] class Collector(object): def __getattr__(self, name): calls.append(name) f(Collector()) return calls PK!stories/_compat.pytry: from enum import Enum, EnumMeta except ImportError: # We are on Python 2.7 and enum34 package is not installed. class Enum(object): pass class EnumMeta(object): pass PK!<$stories/_context.pyfrom collections import OrderedDict from ._contract import deny_attribute_assign, deny_attribute_delete class Context(object): def __init__(self, ns, history): self.__dict__["_Context__ns"] = OrderedDict(ns) self.__dict__["_Context__history"] = history self.__dict__["_Context__lines"] = ["Story argument"] * len(ns) def __getattr__(self, name): return self.__ns[name] def __setattr__(self, name, value): deny_attribute_assign() def __delattr__(self, name): deny_attribute_delete() def __repr__(self): return history_representation(self) + "\n\n" + context_representation(self) def __dir__(self): spec = type("Context", (object,), {}) parent = set(dir(spec())) current = set(self.__dict__) - { "_Context__ns", "_Context__history", "_Context__lines", } scope = set(self.__ns) attributes = sorted(parent | current | scope) return attributes def assign_namespace(ctx, method, kwargs): ctx._Context__ns.update(kwargs) line = "Set by %s.%s" % (method.__self__.__class__.__name__, method.__name__) ctx._Context__lines.extend([line] * len(kwargs)) def history_representation(ctx): result = "\n".join(ctx._Context__history.lines) return result def context_representation(ctx): if not ctx._Context__lines: return "Context()" items = [ "%s = %s" % (key, repr(value)) for (key, value) in ctx._Context__ns.items() ] longest = max(map(len, items)) lines = [ " %s # %s" % (item.ljust(longest), line) for item, line in zip(items, ctx._Context__lines) ] return "\n".join(["Context:"] + lines) PK!H^  stories/_contract.pyfrom .exceptions import ContextContractError def make_contract(cls_name, name, arguments): return Contract(cls_name, name, arguments) def validate_arguments(arguments, args, kwargs): # FIXME: Should be a method of the `Contract` class. assert not (args and kwargs) if args: assert len(arguments) == len(args) return [(k, v) for k, v in zip(arguments, args)] assert set(arguments) == set(kwargs) return [(k, kwargs[k]) for k in arguments] class Contract(object): def __init__(self, cls_name, name, arguments): self.cls_name = cls_name self.name = name self.arguments = arguments def check_story_arguments(self, ctx): missed = set(self.arguments) - set(ctx._Context__ns) if missed: message = missed_variable_template.format( missed=", ".join(sorted(missed)), cls=self.cls_name, method=self.name, arguments=", ".join(self.arguments), ctx=ctx, ) raise ContextContractError(message) def check_success_statement(self, method, ctx, ns): tries_to_override = set(ctx._Context__ns) & set(ns) if tries_to_override: message = variable_override_template.format( variables=", ".join(map(repr, sorted(tries_to_override))), cls=method.__self__.__class__.__name__, method=method.__name__, ) raise ContextContractError(message) def deny_attribute_assign(): raise ContextContractError(assign_attribute_template) def deny_attribute_delete(): raise ContextContractError(delete_attribute_template) missed_variable_template = """ These variables are missing from the context: {missed} Story method: {cls}.{method} Story arguments: {arguments} {ctx!r} """.strip() variable_override_template = """ These variables are already present in the context: {variables} Function returned value: {cls}.{method} Use different names for Success() keyword arguments. """.strip() assign_attribute_template = """ Context object is immutable. Use Success() keyword arguments to expand its scope. """.strip() delete_attribute_template = """ Context object is immutable. Variables can not be removed from Context. """.strip() PK!stories/_exec/__init__.pyPK!4bpCstories/_exec/function.pyfrom .._context import assign_namespace from .._marker import BeginningOfStory, EndOfStory from .._return import Failure, Result, Skip, Success def execute(runner, ctx, history, methods): skipped = 0 for method, contract, protocol in methods: method_type = type(method) if skipped > 0: if method_type is EndOfStory: skipped -= 1 elif method_type is BeginningOfStory: skipped += 1 continue history.before_call(method.__name__) try: result = method(ctx) except Exception as error: history.on_error(error.__class__.__name__) raise restype = type(result) assert restype in (Result, Success, Failure, Skip) if restype is Failure: try: protocol.check_return_statement(method, result.reason) except Exception as error: history.on_error(error.__class__.__name__) raise history.on_failure(result.reason) return runner.got_failure(ctx, method.__name__, result.reason) if restype is Result: history.on_result(result.value) return runner.got_result(result.value) if restype is Skip: history.on_skip() skipped = 1 continue if method_type is BeginningOfStory: try: contract.check_story_arguments(ctx) except Exception as error: history.on_error(error.__class__.__name__) raise history.on_substory_start() continue if method_type is EndOfStory: history.on_substory_end() continue try: contract.check_success_statement(method, ctx, result.kwargs) except Exception as error: history.on_error(error.__class__.__name__) raise assign_namespace(ctx, method, result.kwargs) return runner.finished() PK!׋ stories/_failures.pyfrom ._compat import Enum, EnumMeta from .exceptions import FailureProtocolError # Data type. def check_data_type(failures): if failures is None: return if isinstance(failures, EnumMeta): return if isinstance(failures, (list, tuple, set, frozenset)) and all( isinstance(failure, str) for failure in failures ): return message = wrong_type_template.format(failures=failures) raise FailureProtocolError(message) def failures_representation(failures): if isinstance(failures, EnumMeta): return ", ".join(map(repr, failures.__members__.values())) elif isinstance(failures, (list, tuple, set, frozenset)): return ", ".join(map(repr, failures)) elif failures is None: return "None" def collection_contains(reason, failures): return reason in failures def collection_compare(a, b): return a == b def enumeration_contains(reason, failures): return isinstance(reason, Enum) and reason.name in failures.__members__ def enumeration_compare(a, b): return a.name == b.name # Execute. def make_exec_protocol(failures): if isinstance(failures, EnumMeta): return NotNullExecProtocol(failures, enumeration_contains) elif isinstance(failures, (list, tuple, set, frozenset)): return NotNullExecProtocol(failures, collection_contains) elif failures is None: return NullExecProtocol() class NullExecProtocol(object): def check_return_statement(self, method, reason): if reason: message = null_protocol_template.format( reason=reason, cls=method.__self__.__class__.__name__, method=method.__name__, ) raise FailureProtocolError(message) class DisabledNullExecProtocol(NullExecProtocol): def check_return_statement(self, method, reason): if not reason: message = disabled_null_template.format( cls=method.__self__.__class__.__name__, method=method.__name__ ) raise FailureProtocolError(message) super(DisabledNullExecProtocol, self).check_return_statement(method, reason) class NotNullExecProtocol(object): def __init__(self, failures, contains_func): self.failures = failures self.contains_func = contains_func def check_return_statement(self, method, reason): if not reason: message = null_reason_template.format( available=failures_representation(self.failures), cls=method.__self__.__class__.__name__, method=method.__name__, ) raise FailureProtocolError(message) if not self.contains_func(reason, self.failures): message = wrong_reason_template.format( reason=reason, available=failures_representation(self.failures), cls=method.__self__.__class__.__name__, method=method.__name__, ) raise FailureProtocolError(message) # Run. def make_run_protocol(failures, cls_name, method_name): if isinstance(failures, EnumMeta): return NotNullRunProtocol( cls_name, method_name, failures, enumeration_contains, enumeration_compare ) elif isinstance(failures, (list, tuple, set, frozenset)): return NotNullRunProtocol( cls_name, method_name, failures, collection_contains, collection_compare ) elif failures is None: return NullRunProtocol(cls_name, method_name) class NullRunProtocol(object): def __init__(self, cls_name, method_name): self.cls_name = cls_name self.method_name = method_name def check_failed_because_argument(self, reason): message = null_summary_template.format( cls=self.cls_name, method=self.method_name ) raise FailureProtocolError(message) class NotNullRunProtocol(object): def __init__(self, cls_name, method_name, failures, contains_func, compare_func): self.cls_name = cls_name self.method_name = method_name self.failures = failures self.contains_func = contains_func self.compare_func = compare_func def check_failed_because_argument(self, reason): if not self.contains_func(reason, self.failures): message = wrong_summary_template.format( reason=reason, available=failures_representation(self.failures), cls=self.cls_name, method=self.method_name, ) raise FailureProtocolError(message) def compare_failed_because_argument(self, argument, failure_reason): return self.compare_func(argument, failure_reason) # Wrap. def combine_failures( first_failures, first_cls_name, first_method_name, second_failures, second_cls_name, second_method_name, ): if first_failures is None: return second_failures elif second_failures is None: return first_failures elif isinstance(first_failures, EnumMeta) and isinstance(second_failures, EnumMeta): return Enum( first_failures.__name__, ",".join( list(first_failures.__members__.keys()) + [ failure for failure in second_failures.__members__.keys() if failure not in first_failures.__members__.keys() ] ), ) elif isinstance(first_failures, (list, tuple, set, frozenset)) and isinstance( second_failures, (list, tuple, set, frozenset) ): return first_failures + [ failure for failure in second_failures if failure not in first_failures ] else: message = type_error_template.format( cls=first_cls_name, method=first_method_name, available=failures_representation(first_failures), other_cls=second_cls_name, other_method=second_method_name, other_available=failures_representation(second_failures), ) raise FailureProtocolError(message) def maybe_disable_null_protocol(methods, reasons): if reasons is None: return methods disabled = DisabledNullExecProtocol() return [ (method, contract, disabled if type(protocol) is NullExecProtocol else protocol) for method, contract, protocol in methods ] # Messages. wrong_type_template = """ Unexpected type for story failure protocol: {failures!r} """.strip() wrong_reason_template = """ Failure({reason!r}) failure reason is not allowed by current protocol. Available failures are: {available} Function returned value: {cls}.{method} """.strip() null_reason_template = """ Failure() can not be used in a story with failure protocol. Available failures are: {available} Function returned value: {cls}.{method} Use one of them as Failure() argument. """.strip() null_protocol_template = """ Failure({reason!r}) can not be used in a story without failure protocol. Function returned value: {cls}.{method} Use 'failures' story method to define failure protocol. """.strip() wrong_summary_template = """ 'failed_because' method got argument mismatching failure protocol: {reason!r} Available failures are: {available} Story returned result: {cls}.{method} """.strip() null_summary_template = """ 'failed_because' method can not be used with story defined without failure protocol. Story returned result: {cls}.{method} Use 'failures' story method to define failure protocol. """.strip() type_error_template = """ Story and substory failure protocols has incompatible types: Story method: {cls}.{method} Story failure protocol: {available} Substory method: {other_cls}.{other_method} Substory failure protocol: {other_available} """.strip() disabled_null_template = """ Failure() can not be used in a story composition. Different types of failure protocol were used in parent and substory definitions. Function returned value: {cls}.{method} Use 'failures' story method to define failure protocol. """.strip() PK!s[stories/_history.pyclass History(object): def __init__(self): self.indent = 0 self.lines = [] def before_call(self, method_name): self.lines.append(" " * self.indent + method_name) def on_result(self, value): self.lines[-1] += " (returned: " + repr(value) + ")" def on_failure(self, reason): if reason: self.lines[-1] += " (failed: " + repr(reason) + ")" else: self.lines[-1] += " (failed)" def on_skip(self): self.lines[-1] += " (skipped)" self.indent -= 1 def on_error(self, error_name): self.lines[-1] += " (errored: " + error_name + ")" def on_substory_start(self): self.indent += 1 def on_substory_end(self): self.lines.pop() self.indent -= 1 PK!yssstories/_marker.pyfrom ._return import Success class BeginningOfStory(object): def __init__(self, cls_name, name): self.cls_name = cls_name self.name = name self.parent_name = None self.same_object = None def __call__(self, ctx): return Success() @property def __name__(self): if self.parent_name is None: return self.cls_name + "." + self.name elif self.same_object: return self.parent_name else: return self.parent_name + " (" + self.cls_name + "." + self.name + ")" def set_parent(self, parent_name, same_object): self.parent_name = parent_name self.same_object = same_object class EndOfStory(object): def __init__(self, is_empty): self.is_empty = is_empty def __call__(self, ctx): return Success() __name__ = "end_of_story" PK!;C stories/_mounted.pyfrom ._context import Context from ._contract import validate_arguments from ._exec import function from ._failures import make_run_protocol from ._history import History from ._marker import BeginningOfStory, EndOfStory from ._run import Call, Run class ClassMountedStory(object): def __init__(self, cls, name, collected, failures): self.cls = cls self.name = name self.collected = collected self.failures = failures def __repr__(self): result = [self.cls.__name__ + "." + self.name] if self.collected: for name in self.collected: attr = getattr(self.cls, name, None) if type(attr) is ClassMountedStory: result.append(" " + attr.name) result.extend([" " + line for line in repr(attr).splitlines()[1:]]) else: defined = "" if attr else " ??" result.append(" " + name + defined) else: result.append(" ") return "\n".join(result) class MountedStory(object): def __init__(self, obj, cls_name, name, arguments, methods, failures): self.obj = obj self.cls_name = cls_name self.name = name self.arguments = arguments self.methods = methods self.failures = failures def __call__(self, *args, **kwargs): history = History() ctx = Context(validate_arguments(self.arguments, args, kwargs), history) runner = Call() return function.execute(runner, ctx, history, self.methods) def run(self, *args, **kwargs): history = History() ctx = Context(validate_arguments(self.arguments, args, kwargs), history) run_protocol = make_run_protocol(self.failures, self.cls_name, self.name) runner = Run(run_protocol) return function.execute(runner, ctx, history, self.methods) def __repr__(self): result = [] indent = 0 for method, contract, protocol in self.methods: method_type = type(method) if method_type is EndOfStory: if method.is_empty: result.append(" " * indent + "") indent -= 1 else: result.append(" " * indent + method.__name__) if method_type is BeginningOfStory: indent += 1 return "\n".join(result) PK!ǣstories/_return.pyclass Result(object): def __init__(self, value=None): self.value = value def __repr__(self): return "Result(" + repr(self.value) + ")" class Success(object): def __init__(self, **kwargs): self.kwargs = kwargs def __repr__(self): return ( "Success(" + ", ".join([k + "=" + repr(v) for k, v in self.kwargs.items()]) + ")" ) class Failure(object): def __init__(self, reason=None): self.reason = reason def __repr__(self): reason = repr(self.reason) if self.reason else "" return "Failure(" + reason + ")" class Skip(object): def __repr__(self): return "Skip()" PK!stories/_run.pyfrom ._summary import FailureSummary, SuccessSummary from .exceptions import FailureError class Call(object): def got_failure(self, ctx, method_name, reason): raise FailureError(reason) def got_result(self, value): return value def finished(self): pass class Run(object): def __init__(self, protocol): self.protocol = protocol def got_failure(self, ctx, method_name, reason): return FailureSummary(self.protocol, ctx, method_name, reason) def got_result(self, value): return SuccessSummary(self.protocol, value) def finished(self): return SuccessSummary(self.protocol, None) PK!  stories/_story.pyfrom ._argument import get_arguments from ._collect import collect_story from ._failures import check_data_type from ._mounted import ClassMountedStory, MountedStory from ._wrap import wrap_story class Story(object): def __init__(self, f): self.name = f.__name__ self.arguments = get_arguments(f) self.collected = collect_story(f) self.failures(None) def __get__(self, obj, cls): if obj is None: return ClassMountedStory(cls, self.name, self.collected, self.failures) else: methods, failures = wrap_story( self.arguments, self.collected, cls.__name__, self.name, obj, self.__failures, ) return MountedStory( obj, cls.__name__, self.name, self.arguments, methods, failures ) def failures(self, failures): check_data_type(failures) self.__failures = failures return failures PK!Ӂstories/_summary.pyclass FailureSummary(object): def __init__(self, protocol, ctx, failed_method, reason): self.__protocol = protocol self.is_success = False self.is_failure = True self.ctx = ctx self.__failed_method = failed_method self.__failure_reason = reason def failed_on(self, method_name): return method_name == self.__failed_method def failed_because(self, reason): self.__protocol.check_failed_because_argument(reason) return self.__protocol.compare_failed_because_argument( reason, self.__failure_reason ) @property def value(self): raise AssertionError def __repr__(self): return "Failure()" class SuccessSummary(object): def __init__(self, protocol, value): self.__protocol = protocol self.is_success = True self.is_failure = False self.value = value def failed_on(self, method_name): return False def failed_because(self, reason): self.__protocol.check_failed_because_argument(reason) return False def __repr__(self): return "Success()" PK!^^stories/_wrap.pyfrom ._contract import make_contract from ._failures import combine_failures, make_exec_protocol, maybe_disable_null_protocol from ._marker import BeginningOfStory, EndOfStory from ._mounted import MountedStory def wrap_story(arguments, collected, cls_name, story_name, obj, failures): contract = make_contract(cls_name, story_name, arguments) protocol = make_exec_protocol(failures) methods = [(BeginningOfStory(cls_name, story_name), contract, protocol)] for name in collected: attr = getattr(obj, name) if type(attr) is not MountedStory: methods.append((attr, contract, protocol)) continue failures = combine_failures( failures, cls_name, story_name, attr.failures, attr.cls_name, attr.name ) # FIXME: Is there a way to avoid this modification? attr.methods[0][0].set_parent(name, attr.obj is obj) methods.extend(attr.methods) methods.append((EndOfStory(is_empty=not collected), contract, protocol)) methods = maybe_disable_null_protocol(methods, failures) return methods, failures PK!stories/contrib/__init__.pyPK!*stories/contrib/debug_toolbars/__init__.pyPK!1stories/contrib/debug_toolbars/django/__init__.pyPK!AvG/stories/contrib/debug_toolbars/django/panels.py""" stories.contrib.debug_toolbars.django.panels -------------------------------------------- This module contains integration with Django Debug Toolbar. :copyright: (c) 2018-2019 dry-python team. :license: BSD, see LICENSE for more details. """ import stories._context from debug_toolbar.panels import Panel from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ungettext_lazy as __ origin_context_init = stories._context.Context.__init__ def track_context(storage): def wrapper(ctx, ns, history): origin_context_init(ctx, ns, history) storage.append(ctx) return wrapper class StoriesPanel(Panel): # Implement the Panel API template = "stories/debug_toolbar/stories_panel.html" nav_title = _("Stories") @property def nav_subtitle(self): count = len(self.storage) return __("%(count)d call", "%(count)d calls", count) % {"count": count} @property def title(self): count = len(self.storage) return __( "Context and execution path of %(count)d story", "Context and execution path of %(count)d stories", count, ) % {"count": count} # Implement the Collector. def __init__(self, *args, **kwargs): super(StoriesPanel, self).__init__(*args, **kwargs) self.storage = [] def enable_instrumentation(self): stories._context.Context.__init__ = track_context(self.storage) def disable_instrumentation(self): stories._context.Context.__init__ = origin_context_init def generate_stats(self, request, response): self.record_stats({"stories": self.storage}) PK!IRXstories/contrib/debug_toolbars/django/templates/stories/debug_toolbar/stories_panel.html{% load i18n %} {% if stories %} {% for ctx in stories %} {% endfor %}
  {% trans 'Context' %}
 
{{ ctx }}
{% else %}

{% trans 'No story calls were recorded during this request.' %}

{% endif %} PK!UUstories/contrib/pytest.py""" stories.contrib.pytest ---------------------- This module contains integration with PyTest framework. :copyright: (c) 2018-2019 dry-python team. :license: BSD, see LICENSE for more details. """ import linecache import sys import textwrap from _pytest.config import hookimpl import stories._context origin_context_init = stories._context.Context.__init__ def track_context(storage): def wrapper(ctx, ns, history): origin_context_init(ctx, ns, history) storage.append((get_test_source(*get_test_call()), ctx)) return wrapper def get_test_call(): f = sys._getframe() while True: if ( "@py_builtins" in f.f_globals and "@pytest_ar" in f.f_globals and f.f_code.co_name.startswith("test_") and f.f_code.co_filename != __file__ ): return f.f_code.co_filename, f.f_lineno elif not f.f_back: raise Exception("Can not find running test") else: f = f.f_back def get_test_source(filename, lineno): start = max(1, lineno - 3) end = lineno + 3 adjust_to = len(str(end)) lines = [linecache.getline(filename, no) for no in range(start, end)] text = textwrap.dedent("".join(lines)) src = [] for num, line in zip(range(start, end), text.splitlines()): sep = "->" if num == lineno else " " src.append((" %s %s %s" % (str(num).rjust(adjust_to), sep, line)).rstrip()) src = "\n".join(src) return src @hookimpl(hookwrapper=True) def pytest_runtest_call(item): storage = [] stories._context.Context.__init__ = track_context(storage) yield stories._context.Context.__init__ = origin_context_init for i, (src, ctx) in enumerate(storage, 1): output = "\n\n".join([src, repr(ctx)]) item.add_report_section("call", "story #%d" % (i,), output) PK!"stories/contrib/sentry/__init__.pyPK!Pg0%stories/contrib/sentry/breadcrumbs.py""" stories.contrib.sentry.breadcrumbs ---------------------------------- This module contains integration with Sentry. :copyright: (c) 2018-2019 dry-python team. :license: BSD, see LICENSE for more details. """ import stories._context from raven.breadcrumbs import libraryhook, record origin_context_init = stories._context.Context.__init__ @libraryhook("stories") def track_context(): def wrapper(ctx, ns, history): origin_context_init(ctx, ns, history) record( processor=lambda data: data.update( {"category": "story", "message": repr(ctx)} ) ) stories._context.Context.__init__ = wrapper PK!lL;oo stories/contrib/sentry/django.pyimport stories.contrib.sentry.breadcrumbs # noqa from raven.contrib.django.client import DjangoClient # noqa PK!Qbbstories/exceptions.py""" stories.exceptions ------------------ This module contains errors definitions user can handle. :copyright: (c) 2018-2019 dry-python team. :license: BSD, see LICENSE for more details. """ class StoryError(Exception): pass class FailureError(StoryError): def __init__(self, reason): self.__reason = reason super(FailureError, self).__init__() def __repr__(self): reason = repr(self.__reason) if self.__reason else "" return "FailureError(" + reason + ")" class FailureProtocolError(StoryError): pass class ContextContractError(StoryError): pass PK!DDstories/shortcuts.py""" stories.shortcuts ----------------- This module contains convenient functions to reduce boilerplate code. :copyright: (c) 2018-2019 dry-python team. :license: BSD, see LICENSE for more details. """ from ._mounted import ClassMountedStory def failures_in(cls, *args): def setter(failures): for attrname in dir(cls): attribute = getattr(cls, attrname) if type(attribute) is ClassMountedStory: attribute.failures(failures) return failures if args: return setter(*args) else: return setter PK!H~g#+'stories-0.10.dist-info/entry_points.txt.,I-.14*./L-zy%EIz\\PK!etstories-0.10.dist-info/LICENSECopyright 2018-2019 dry-python team Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PK!H|n-WYstories-0.10.dist-info/WHEEL A н#Z;/" bFF]xzwK;<*mTֻ0*Ri.4Vm0[H, JPK!H!!Ystories-0.10.dist-info/METADATAW[o6~ׯ:Eia-{rhRCCR%_҄/sΝ Ka\PeLY'+xL(-8Szv`-_Fj7D,j+b4+%.]4O&z 5 |B^ NDaw[?H:ԭg U东x5v&v`ڥ 'A(6ڄupkwṉ;yf8SYr:` PsQhЖ#I ࿃i InDsi킹BOD7C?QSuOTMVk@RjrTRm]L]1B y[tR6YPNB}|l_>o@D]!F{2<~zp2-T`rfPS"kG@ ň&W0@c\jUt0*YcɬN1 _# jiĎY޾?܈Iydx91ycnO6h7ڌ'=vM):$wݡK!U|$v;`pjr@$i}GA&Rpb/t\瞓-:I99uqP-b66WS+m D8$$}?`kC޻V.mex`,m~v0ʎTYoӸoQ؊x|رc/ju`=O`tBmryr6&]:yC|]mdlU7J ݗ4Mcײ!+Q 1T3yC6fC F۶fچ(P/Z.Qn~qq;szر 7 /dOs/:ϼ/}PK!H(_xd stories-0.10.dist-info/RECORDɲF}a$@YD؉reGQ]_}O8C{^}N#wYh^U>_z5Ro.m otfYNںrF]\@62{(NJ>;5dcX)imUឪ$QϞҧn-wz 'Jlc $K4 cXħ[&wlYC+>kGqgL1EWkq YK%-ld|(zfon. ԪNx qxV8"kVb$]A =KxcqIf|\27ᔷ͎K`EEN^u#;+`?zJ!Gf0S2()&~^C<&9oܴB3{y0"ZϝP?Zm=jB33ܤLd Շ@RPN5 _xbSw>-I >}(t'pD;!V"yKPupkYg^݋ K5< /vepK-}*ET0j=0m.ڥ=*6l:ԃ^VfWJAP̏F\8ffe5Y!erߟhP< @\j^rc1RK%fI^\ްA[#ua|g- )֍R*CGrI)#Ź5hnq6ʔ+ .k+k7%׻Q7iMM\qSP⌶{j}:B|<!GT{m]$X3iWKBWRT$hSEƊs@vy[0ON`lͽ6+.XVS,C_Mu^KFd`Ax|dHp{4Sis$@iK .,Sgd#!XN>F%Nk@i~݂u(?eF Qn}n좦ed +T|Ghng?H^xy/git '6=%<ZF5xV58OPK!ᡘggstories/__init__.pyPK!stories/_argument.pyPK!/@stories/_collect.pyPK!^stories/_compat.pyPK!<$Zstories/_context.pyPK!H^  ^ stories/_contract.pyPK!stories/_exec/__init__.pyPK!4bpCstories/_exec/function.pyPK!׋ stories/_failures.pyPK!s[C=stories/_history.pyPK!yss@stories/_marker.pyPK!;C ,Dstories/_mounted.pyPK!ǣMstories/_return.pyPK!Pstories/_run.pyPK!  Sstories/_story.pyPK!ӁWstories/_summary.pyPK!^^\stories/_wrap.pyPK!astories/contrib/__init__.pyPK!*Uastories/contrib/debug_toolbars/__init__.pyPK!1astories/contrib/debug_toolbars/django/__init__.pyPK!AvG/astories/contrib/debug_toolbars/django/panels.pyPK!IRXhstories/contrib/debug_toolbars/django/templates/stories/debug_toolbar/stories_panel.htmlPK!UUdkstories/contrib/pytest.pyPK!"rstories/contrib/sentry/__init__.pyPK!Pg0%0sstories/contrib/sentry/breadcrumbs.pyPK!lL;oo vstories/contrib/sentry/django.pyPK!Qbbvstories/exceptions.pyPK!DDUystories/shortcuts.pyPK!H~g#+'{stories-0.10.dist-info/entry_points.txtPK!et3|stories-0.10.dist-info/LICENSEPK!H|n-WYtstories-0.10.dist-info/WHEELPK!H!!Ystories-0.10.dist-info/METADATAPK!H(_xd stories-0.10.dist-info/RECORDPK!!w `