def redirects_handler(*args, **kwargs):
    """Fixes Admin contrib redirects compatibility problems
    introduced in Django 1.4 by url handling changes.

    """
    path = args[0].path
    shift = '../'

    if 'delete' in path:
        # Weird enough 'delete' is not handled by TreeItemAdmin::response_change().
        shift += '../'
    elif 'history' in path:
        if 'item_id' not in kwargs:
            # Encountered request from history page to return to tree layout page.
            shift += '../'

    return HttpResponseRedirect(path + shift)

def redirects_handler ( * args , ** kwargs ) : path = args [ 0 ] . path shift = '../' if 'delete' in path : shift += '../' elif 'history' in path : if 'item_id' not in kwargs : shift += '../' return HttpResponseRedirect ( path + shift )

Fixes Admin contrib redirects compatibility problems introduced in Django 1 . 4 by url handling changes .


def _redirect(self, request, response):
        """Generic redirect for item editor."""

        if '_addanother' in request.POST:
            return HttpResponseRedirect('../item_add/')

        elif '_save' in request.POST:
            return HttpResponseRedirect('../')

        elif '_continue' in request.POST:
            return response

        return HttpResponseRedirect('')

def _redirect ( self , request , response ) : if '_addanother' in request . POST : return HttpResponseRedirect ( '../item_add/' ) elif '_save' in request . POST : return HttpResponseRedirect ( '../' ) elif '_continue' in request . POST : return response return HttpResponseRedirect ( '' )


def response_add(self, request, obj, post_url_continue=None, **kwargs):
        """Redirects to the appropriate items' 'continue' page on item add.

        As we administer tree items within tree itself, we
        should make some changes to redirection process.

        """
        if post_url_continue is None:
            post_url_continue = '../item_%s/' % obj.pk

        return self._redirect(request, super(TreeItemAdmin, self).response_add(request, obj, post_url_continue))

def response_add ( self , request , obj , post_url_continue = None , ** kwargs ) : if post_url_continue is None : post_url_continue = '../item_%s/' % obj . pk return self . _redirect ( request , super ( TreeItemAdmin , self ) . response_add ( request , obj , post_url_continue ) )

Redirects to the appropriate items continue page on item add .


def response_change(self, request, obj, **kwargs):
        """Redirects to the appropriate items' 'add' page on item change.

        As we administer tree items within tree itself, we
        should make some changes to redirection process.

        """
        return self._redirect(request, super(TreeItemAdmin, self).response_change(request, obj))

def response_change ( self , request , obj , ** kwargs ) : return self . _redirect ( request , super ( TreeItemAdmin , self ) . response_change ( request , obj ) )

Redirects to the appropriate items add page on item change .


def get_form(self, request, obj=None, **kwargs):
        """Returns modified form for TreeItem model.
        'Parent' field choices are built by sitetree itself.

        """
        if obj is not None and obj.parent is not None:
            self.previous_parent = obj.parent
            previous_parent_id = self.previous_parent.id
        else:
            previous_parent_id = None

        my_choice_field = TreeItemChoiceField(self.tree, initial=previous_parent_id)
        form = super(TreeItemAdmin, self).get_form(request, obj, **kwargs)
        my_choice_field.label = form.base_fields['parent'].label
        my_choice_field.help_text = form.base_fields['parent'].help_text
        my_choice_field.widget = form.base_fields['parent'].widget
        # Replace 'parent' TreeItem field with new appropriate one
        form.base_fields['parent'] = my_choice_field

        # Try to resolve all currently registered url names including those in namespaces.
        if not getattr(self, 'known_url_names', False):
            self.known_url_names = []
            self.known_url_rules = []
            resolver = get_resolver(get_urlconf())
            for ns, (url_prefix, ns_resolver) in resolver.namespace_dict.items():
                if ns != 'admin':
                    self._stack_known_urls(ns_resolver.reverse_dict, ns)
            self._stack_known_urls(resolver.reverse_dict)
            self.known_url_rules = sorted(self.known_url_rules)

        form.known_url_names_hint = _(
            'You are seeing this warning because "URL as Pattern" option is active and pattern entered above '
            'seems to be invalid. Currently registered URL pattern names and parameters: ')
        form.known_url_names = self.known_url_names
        form.known_url_rules = self.known_url_rules
        return form

Returns modified form for TreeItem model . Parent field choices are built by sitetree itself .


def get_tree(self, request, tree_id, item_id=None):
        """Fetches Tree for current or given TreeItem."""
        if tree_id is None:
            tree_id = self.get_object(request, item_id).tree_id
        self.tree = MODEL_TREE_CLASS._default_manager.get(pk=tree_id)
        self.tree.verbose_name_plural = self.tree._meta.verbose_name_plural
        self.tree.urls = _TREE_URLS
        return self.tree

def get_tree ( self , request , tree_id , item_id = None ) : if tree_id is None : tree_id = self . get_object ( request , item_id ) . tree_id self . tree = MODEL_TREE_CLASS . _default_manager . get ( pk = tree_id ) self . tree . verbose_name_plural = self . tree . _meta . verbose_name_plural self . tree . urls = _TREE_URLS return self . tree

Fetches Tree for current or given TreeItem .


def item_move(self, request, tree_id, item_id, direction):
        """Moves item up or down by swapping 'sort_order' field values of neighboring items."""
        current_item = MODEL_TREE_ITEM_CLASS._default_manager.get(pk=item_id)
        if direction == 'up':
            sort_order = 'sort_order'
        else:
            sort_order = '-sort_order'

        siblings = MODEL_TREE_ITEM_CLASS._default_manager.filter(
            parent=current_item.parent,
            tree=current_item.tree
        ).order_by(sort_order)

        previous_item = None
        for item in siblings:
            if item != current_item:
                previous_item = item
            else:
                break

        if previous_item is not None:
            current_item_sort_order = current_item.sort_order
            previous_item_sort_order = previous_item.sort_order

            current_item.sort_order = previous_item_sort_order
            previous_item.sort_order = current_item_sort_order

            current_item.save()
            previous_item.save()

        return HttpResponseRedirect('../../')

def item_move ( self , request , tree_id , item_id , direction ) : current_item = MODEL_TREE_ITEM_CLASS . _default_manager . get ( pk = item_id ) if direction == 'up' : sort_order = 'sort_order' else : sort_order = '-sort_order' siblings = MODEL_TREE_ITEM_CLASS . _default_manager . filter ( parent = current_item . parent , tree = current_item . tree ) . order_by ( sort_order ) previous_item = None for item in siblings : if item != current_item : previous_item = item else : break if previous_item is not None : current_item_sort_order = current_item . sort_order previous_item_sort_order = previous_item . sort_order current_item . sort_order = previous_item_sort_order previous_item . sort_order = current_item_sort_order current_item . save ( ) previous_item . save ( ) return HttpResponseRedirect ( '../../' )

Moves item up or down by swapping sort_order field values of neighboring items .


def save_model(self, request, obj, form, change):
        """Saves TreeItem model under certain Tree.
        Handles item's parent assignment exception.

        """
        if change:
            # No, you're not allowed to make item parent of itself
            if obj.parent is not None and obj.parent.id == obj.id:
                obj.parent = self.previous_parent
                messages.warning(
                    request, _("Item's parent left unchanged. Item couldn't be parent to itself."), '', True)
        obj.tree = self.tree
        obj.save()

Saves TreeItem model under certain Tree . Handles item s parent assignment exception .


def get_urls(self):
        """Manages not only TreeAdmin URLs but also TreeItemAdmin URLs."""
        urls = super(TreeAdmin, self).get_urls()

        prefix_change = 'change/' if DJANGO_POST_19 else ''

        sitetree_urls = [
            url(r'^change/$', redirects_handler, name=get_tree_item_url_name('changelist')),

            url(r'^((?P<tree_id>\d+)/)?%sitem_add/$' % prefix_change,
                self.admin_site.admin_view(self.tree_admin.item_add), name=get_tree_item_url_name('add')),

            url(r'^(?P<tree_id>\d+)/%sitem_(?P<item_id>\d+)/$' % prefix_change,
                self.admin_site.admin_view(self.tree_admin.item_edit), name=get_tree_item_url_name('change')),

            url(r'^%sitem_(?P<item_id>\d+)/$' % prefix_change,
                self.admin_site.admin_view(self.tree_admin.item_edit), name=get_tree_item_url_name('change')),

            url(r'^((?P<tree_id>\d+)/)?%sitem_(?P<item_id>\d+)/delete/$' % prefix_change,
                self.admin_site.admin_view(self.tree_admin.item_delete), name=get_tree_item_url_name('delete')),

            url(r'^((?P<tree_id>\d+)/)?%sitem_(?P<item_id>\d+)/history/$' % prefix_change,
                self.admin_site.admin_view(self.tree_admin.item_history), name=get_tree_item_url_name('history')),

            url(r'^(?P<tree_id>\d+)/%sitem_(?P<item_id>\d+)/move_(?P<direction>(up|down))/$' % prefix_change,
                self.admin_site.admin_view(self.tree_admin.item_move), name=get_tree_item_url_name('move')),
        ]

        if not DJANGO_POST_19:
            sitetree_urls = patterns_func('', *sitetree_urls)

        if SMUGGLER_INSTALLED:
            sitetree_urls += (url(r'^dump_all/$', self.admin_site.admin_view(self.dump_view), name='sitetree_dump'),)

        return sitetree_urls + urls

Manages not only TreeAdmin URLs but also TreeItemAdmin URLs .


def dump_view(cls, request):
        """Dumps sitetrees with items using django-smuggler.

        :param request:
        :return:
        """
        from smuggler.views import dump_to_response
        return dump_to_response(request, [MODEL_TREE, MODEL_TREE_ITEM], filename_prefix='sitetrees')

def dump_view ( cls , request ) : from smuggler . views import dump_to_response return dump_to_response ( request , [ MODEL_TREE , MODEL_TREE_ITEM ] , filename_prefix = 'sitetrees' )

Dumps sitetrees with items using django - smuggler .


def tree(alias, title='', items=None, **kwargs):
    """Dynamically creates and returns a sitetree.

    :param str|unicode alias:
    :param str|unicode title:
    :param iterable items: dynamic sitetree items objects created by `item` function.
    :param kwargs: Additional arguments to pass to tree item initializer.

    :rtype: TreeBase
    """
    tree_obj = get_tree_model()(alias=alias, title=title, **kwargs)
    tree_obj.id = generate_id_for(tree_obj)
    tree_obj.is_dynamic = True

    if items is not None:
        tree_obj.dynamic_items = []
        def traverse(items):
            for item in items:
                item.tree = tree_obj
                tree_obj.dynamic_items.append(item)
                if hasattr(item, 'dynamic_children'):
                    traverse(item.dynamic_children)

        traverse(items)
    return tree_obj

def tree ( alias , title = '' , items = None , ** kwargs ) : tree_obj = get_tree_model ( ) ( alias = alias , title = title , ** kwargs ) tree_obj . id = generate_id_for ( tree_obj ) tree_obj . is_dynamic = True if items is not None : tree_obj . dynamic_items = [ ] def traverse ( items ) : for item in items : item . tree = tree_obj tree_obj . dynamic_items . append ( item ) if hasattr ( item , 'dynamic_children' ) : traverse ( item . dynamic_children ) traverse ( items ) return tree_obj

Dynamically creates and returns a sitetree .


def item(
    title, url, children=None, url_as_pattern=True, hint='', alias='', description='',
    in_menu=True, in_breadcrumbs=True, in_sitetree=True,
    access_loggedin=False, access_guest=False,
    access_by_perms=None, perms_mode_all=True, **kwargs):
    """Dynamically creates and returns a sitetree item object.

    :param str|unicode title:

    :param str|unicode url:

    :param list, set children: a list of children for tree item. Children should also be created by `item` function.

    :param bool url_as_pattern: consider URL as a name of a named URL

    :param str|unicode hint: hints are usually shown to users

    :param str|unicode alias: item name to address it from templates

    :param str|unicode description: additional information on item (usually is not shown to users)

    :param bool in_menu: show this item in menus

    :param bool in_breadcrumbs: show this item in breadcrumbs

    :param bool in_sitetree: show this item in sitetrees

    :param bool access_loggedin: show item to logged in users only

    :param bool access_guest: show item to guest users only

    :param list|str||unicode|int, Permission access_by_perms: restrict access to users with these permissions.

        This can be set to one or a list of permission names, IDs or Permission instances.

        Permission names are more portable and should be in a form `<app_label>.<perm_codename>`, e.g.:
            my_app.allow_save

    :param bool perms_mode_all: permissions set interpretation rule:
                True - user should have all the permissions;
                False - user should have any of chosen permissions.

    :rtype: TreeItemBase

    """
    item_obj = get_tree_item_model()(
        title=title, url=url, urlaspattern=url_as_pattern,
        hint=hint, alias=alias, description=description, inmenu=in_menu,
        insitetree=in_sitetree, inbreadcrumbs=in_breadcrumbs,
        access_loggedin=access_loggedin, access_guest=access_guest,
        **kwargs)

    item_obj.id = generate_id_for(item_obj)
    item_obj.is_dynamic = True
    item_obj.dynamic_children = []

    cleaned_permissions = []
    if access_by_perms:
        # Make permissions a list if currently a single object
        if not isinstance(access_by_perms, list):
            access_by_perms = [access_by_perms]

        for perm in access_by_perms:
            if isinstance(perm, six.string_types):
                # Get permission object from string
                try:
                    app, codename = perm.split('.')
                except ValueError:
                    raise ValueError(
                        'Wrong permission string format: supplied - `%s`; '
                        'expected - `<app_name>.<permission_name>`.' % perm)

                try:
                    perm = Permission.objects.get(codename=codename, content_type__app_label=app)
                except Permission.DoesNotExist:
                    raise ValueError('Permission `%s.%s` does not exist.' % (app, codename))

            elif not isinstance(perm, (int, Permission)):
                raise ValueError('Permissions must be given as strings, ints, or `Permission` instances.')

            cleaned_permissions.append(perm)

    item_obj.permissions = cleaned_permissions or []
    item_obj.access_perm_type = item_obj.PERM_TYPE_ALL if perms_mode_all else item_obj.PERM_TYPE_ANY

    if item_obj.permissions:
        item_obj.access_restricted = True

    if children is not None:
        for child in children:
            child.parent = item_obj
            item_obj.dynamic_children.append(child)

    return item_obj

Dynamically creates and returns a sitetree item object .


def import_app_sitetree_module(app):
    """Imports sitetree module from a given app.

    :param str|unicode app: Application name
    :return: module|None
    """
    module_name = settings.APP_MODULE_NAME
    module = import_module(app)
    try:
        sub_module = import_module('%s.%s' % (app, module_name))
        return sub_module
    except ImportError:
        if module_has_submodule(module, module_name):
            raise
        return None

def import_app_sitetree_module ( app ) : module_name = settings . APP_MODULE_NAME module = import_module ( app ) try : sub_module = import_module ( '%s.%s' % ( app , module_name ) ) return sub_module except ImportError : if module_has_submodule ( module , module_name ) : raise return None

Imports sitetree module from a given app .


def get_model_class(settings_entry_name):
    """Returns a certain sitetree model as defined in the project settings.

    :param str|unicode settings_entry_name:
    :rtype: TreeItemBase|TreeBase
    """
    app_name, model_name = get_app_n_model(settings_entry_name)

    try:
        model = apps_get_model(app_name, model_name)
    except (LookupError, ValueError):
        model = None

    if model is None:
        raise ImproperlyConfigured(
            '`SITETREE_%s` refers to model `%s` that has not been installed.' % (settings_entry_name, model_name))

    return model

Returns a certain sitetree model as defined in the project settings .


def from_mapping(
        cls: Type["Config"], mapping: Optional[Mapping[str, Any]] = None, **kwargs: Any
    ) -> "Config":
        """Create a configuration from a mapping.

        This allows either a mapping to be directly passed or as
        keyword arguments, for example,

        .. code-block:: python

            config = {'keep_alive_timeout': 10}
            Config.from_mapping(config)
            Config.form_mapping(keep_alive_timeout=10)

        Arguments:
            mapping: Optionally a mapping object.
            kwargs: Optionally a collection of keyword arguments to
                form a mapping.
        """
        mappings: Dict[str, Any] = {}
        if mapping is not None:
            mappings.update(mapping)
        mappings.update(kwargs)
        config = cls()
        for key, value in mappings.items():
            try:
                setattr(config, key, value)
            except AttributeError:
                pass

        return config

def from_mapping ( cls : Type [ "Config" ] , mapping : Optional [ Mapping [ str , Any ] ] = None , ** kwargs : Any ) -> "Config" : mappings : Dict [ str , Any ] = { } if mapping is not None : mappings . update ( mapping ) mappings . update ( kwargs ) config = cls ( ) for key , value in mappings . items ( ) : try : setattr ( config , key , value ) except AttributeError : pass return config


def from_pyfile(cls: Type["Config"], filename: FilePath) -> "Config":
        """Create a configuration from a Python file.

        .. code-block:: python

            Config.from_pyfile('hypercorn_config.py')

        Arguments:
            filename: The filename which gives the path to the file.
        """
        file_path = os.fspath(filename)
        spec = importlib.util.spec_from_file_location("module.name", file_path)
        module = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(module)  # type: ignore
        return cls.from_object(module)

def from_pyfile ( cls : Type [ "Config" ] , filename : FilePath ) -> "Config" : file_path = os . fspath ( filename ) spec = importlib . util . spec_from_file_location ( "module.name" , file_path ) module = importlib . util . module_from_spec ( spec ) spec . loader . exec_module ( module ) return cls . from_object ( module )

Create a configuration from a Python file .


def from_toml(cls: Type["Config"], filename: FilePath) -> "Config":
        """Load the configuration values from a TOML formatted file.

        This allows configuration to be loaded as so

        .. code-block:: python

            Config.from_toml('config.toml')

        Arguments:
            filename: The filename which gives the path to the file.
        """
        file_path = os.fspath(filename)
        with open(file_path) as file_:
            data = toml.load(file_)
        return cls.from_mapping(data)

def from_toml ( cls : Type [ "Config" ] , filename : FilePath ) -> "Config" : file_path = os . fspath ( filename ) with open ( file_path ) as file_ : data = toml . load ( file_ ) return cls . from_mapping ( data )

Load the configuration values from a TOML formatted file .


def from_object(cls: Type["Config"], instance: Union[object, str]) -> "Config":
        """Create a configuration from a Python object.

        This can be used to reference modules or objects within
        modules for example,

        .. code-block:: python

            Config.from_object('module')
            Config.from_object('module.instance')
            from module import instance
            Config.from_object(instance)

        are valid.

        Arguments:
            instance: Either a str referencing a python object or the
                object itself.

        """
        if isinstance(instance, str):
            try:
                path, config = instance.rsplit(".", 1)
            except ValueError:
                path = instance
                instance = importlib.import_module(instance)
            else:
                module = importlib.import_module(path)
                instance = getattr(module, config)

        mapping = {
            key: getattr(instance, key)
            for key in dir(instance)
            if not isinstance(getattr(instance, key), types.ModuleType)
        }
        return cls.from_mapping(mapping)

def from_object ( cls : Type [ "Config" ] , instance : Union [ object , str ] ) -> "Config" : if isinstance ( instance , str ) : try : path , config = instance . rsplit ( "." , 1 ) except ValueError : path = instance instance = importlib . import_module ( instance ) else : module = importlib . import_module ( path ) instance = getattr ( module , config ) mapping = { key : getattr ( instance , key ) for key in dir ( instance ) if not isinstance ( getattr ( instance , key ) , types . ModuleType ) } return cls . from_mapping ( mapping )

Create a configuration from a Python object .


def create_attrs_for_span(
    sample_rate=100.0,
    trace_id=None,
    span_id=None,
    use_128bit_trace_id=False,
):
    """Creates a set of zipkin attributes for a span.

    :param sample_rate: Float between 0.0 and 100.0 to determine sampling rate
    :type sample_rate: float
    :param trace_id: Optional 16-character hex string representing a trace_id.
                    If this is None, a random trace_id will be generated.
    :type trace_id: str
    :param span_id: Optional 16-character hex string representing a span_id.
                    If this is None, a random span_id will be generated.
    :type span_id: str
    :param use_128bit_trace_id: If true, generate 128-bit trace_ids
    :type use_128bit_trace_id: boolean
    """
    # Calculate if this trace is sampled based on the sample rate
    if trace_id is None:
        if use_128bit_trace_id:
            trace_id = generate_random_128bit_string()
        else:
            trace_id = generate_random_64bit_string()
    if span_id is None:
        span_id = generate_random_64bit_string()
    if sample_rate == 0.0:
        is_sampled = False
    else:
        is_sampled = (random.random() * 100) < sample_rate

    return ZipkinAttrs(
        trace_id=trace_id,
        span_id=span_id,
        parent_span_id=None,
        flags='0',
        is_sampled=is_sampled,
    )

def create_attrs_for_span ( sample_rate = 100.0 , trace_id = None , span_id = None , use_128bit_trace_id = False , ) : if trace_id is None : if use_128bit_trace_id : trace_id = generate_random_128bit_string ( ) else : trace_id = generate_random_64bit_string ( ) if span_id is None : span_id = generate_random_64bit_string ( ) if sample_rate == 0.0 : is_sampled = False else : is_sampled = ( random . random ( ) * 100 ) < sample_rate return ZipkinAttrs ( trace_id = trace_id , span_id = span_id , parent_span_id = None , flags = '0' , is_sampled = is_sampled , )

Creates a set of zipkin attributes for a span .


def create_http_headers_for_new_span(context_stack=None, tracer=None):
    """
    Generate the headers for a new zipkin span.

    .. note::

        If the method is not called from within a zipkin_trace context,
        empty dict will be returned back.

    :returns: dict containing (X-B3-TraceId, X-B3-SpanId, X-B3-ParentSpanId,
                X-B3-Flags and X-B3-Sampled) keys OR an empty dict.
    """
    if tracer:
        zipkin_attrs = tracer.get_zipkin_attrs()
    elif context_stack:
        zipkin_attrs = context_stack.get()
    else:
        zipkin_attrs = get_default_tracer().get_zipkin_attrs()

    if not zipkin_attrs:
        return {}

    return {
        'X-B3-TraceId': zipkin_attrs.trace_id,
        'X-B3-SpanId': generate_random_64bit_string(),
        'X-B3-ParentSpanId': zipkin_attrs.span_id,
        'X-B3-Flags': '0',
        'X-B3-Sampled': '1' if zipkin_attrs.is_sampled else '0',
    }

def create_http_headers_for_new_span ( context_stack = None , tracer = None ) : if tracer : zipkin_attrs = tracer . get_zipkin_attrs ( ) elif context_stack : zipkin_attrs = context_stack . get ( ) else : zipkin_attrs = get_default_tracer ( ) . get_zipkin_attrs ( ) if not zipkin_attrs : return { } return { 'X-B3-TraceId' : zipkin_attrs . trace_id , 'X-B3-SpanId' : generate_random_64bit_string ( ) , 'X-B3-ParentSpanId' : zipkin_attrs . span_id , 'X-B3-Flags' : '0' , 'X-B3-Sampled' : '1' if zipkin_attrs . is_sampled else '0' , }

Generate the headers for a new zipkin span .


def _get_current_context(self):
        """Returns the current ZipkinAttrs and generates new ones if needed.

        :returns: (report_root_timestamp, zipkin_attrs)
        :rtype: (bool, ZipkinAttrs)
        """
        # This check is technically not necessary since only root spans will have
        # sample_rate, zipkin_attrs or a transport set. But it helps making the
        # code clearer by separating the logic for a root span from the one for a
        # child span.
        if self._is_local_root_span:

            # If sample_rate is set, we need to (re)generate a trace context.
            # If zipkin_attrs (trace context) were passed in as argument there are
            # 2 possibilities:
            # is_sampled = False --> we keep the same trace_id but re-roll the dice
            #                        for is_sampled.
            # is_sampled = True  --> we don't want to stop sampling halfway through
            #                        a sampled trace, so we do nothing.
            # If no zipkin_attrs were passed in, we generate new ones and start a
            # new trace.
            if self.sample_rate is not None:

                # If this trace is not sampled, we re-roll the dice.
                if self.zipkin_attrs_override and \
                        not self.zipkin_attrs_override.is_sampled:
                    # This will be the root span of the trace, so we should
                    # set timestamp and duration.
                    return True, create_attrs_for_span(
                        sample_rate=self.sample_rate,
                        trace_id=self.zipkin_attrs_override.trace_id,
                    )

                # If zipkin_attrs_override was not passed in, we simply generate
                # new zipkin_attrs to start a new trace.
                elif not self.zipkin_attrs_override:
                    return True, create_attrs_for_span(
                        sample_rate=self.sample_rate,
                        use_128bit_trace_id=self.use_128bit_trace_id,
                    )

            if self.firehose_handler and not self.zipkin_attrs_override:
                # If it has gotten here, the only thing that is
                # causing a trace is the firehose. So we force a trace
                # with sample rate of 0
                return True, create_attrs_for_span(
                    sample_rate=0.0,
                    use_128bit_trace_id=self.use_128bit_trace_id,
                )

            # If we arrive here it means the sample_rate was not set while
            # zipkin_attrs_override was, so let's simply return that.
            return False, self.zipkin_attrs_override

        else:
            # Check if there's already a trace context in _context_stack.
            existing_zipkin_attrs = self.get_tracer().get_zipkin_attrs()
            # If there's an existing context, let's create new zipkin_attrs
            # with that context as parent.
            if existing_zipkin_attrs:
                return False, ZipkinAttrs(
                    trace_id=existing_zipkin_attrs.trace_id,
                    span_id=generate_random_64bit_string(),
                    parent_span_id=existing_zipkin_attrs.span_id,
                    flags=existing_zipkin_attrs.flags,
                    is_sampled=existing_zipkin_attrs.is_sampled,
                )

        return False, None

def _get_current_context ( self ) : if self . _is_local_root_span : if self . sample_rate is not None : if self . zipkin_attrs_override and not self . zipkin_attrs_override . is_sampled : return True , create_attrs_for_span ( sample_rate = self . sample_rate , trace_id = self . zipkin_attrs_override . trace_id , ) elif not self . zipkin_attrs_override : return True , create_attrs_for_span ( sample_rate = self . sample_rate , use_128bit_trace_id = self . use_128bit_trace_id , ) if self . firehose_handler and not self . zipkin_attrs_override : return True , create_attrs_for_span ( sample_rate = 0.0 , use_128bit_trace_id = self . use_128bit_trace_id , ) return False , self . zipkin_attrs_override else : existing_zipkin_attrs = self . get_tracer ( ) . get_zipkin_attrs ( ) if existing_zipkin_attrs : return False , ZipkinAttrs ( trace_id = existing_zipkin_attrs . trace_id , span_id = generate_random_64bit_string ( ) , parent_span_id = existing_zipkin_attrs . span_id , flags = existing_zipkin_attrs . flags , is_sampled = existing_zipkin_attrs . is_sampled , ) return False , None

Returns the current ZipkinAttrs and generates new ones if needed .


def start(self):
        """Enter the new span context. All annotations logged inside this
        context will be attributed to this span. All new spans generated
        inside this context will have this span as their parent.

        In the unsampled case, this context still generates new span IDs and
        pushes them onto the threadlocal stack, so downstream services calls
        made will pass the correct headers. However, the logging handler is
        never attached in the unsampled case, so the spans are never logged.
        """
        self.do_pop_attrs = False

        report_root_timestamp, self.zipkin_attrs = self._get_current_context()

        # If zipkin_attrs are not set up by now, that means this span is not
        # configured to perform logging itself, and it's not in an existing
        # Zipkin trace. That means there's nothing else to do and it can exit
        # early.
        if not self.zipkin_attrs:
            return self

        self.get_tracer().push_zipkin_attrs(self.zipkin_attrs)
        self.do_pop_attrs = True

        self.start_timestamp = time.time()

        if self._is_local_root_span:
            # Don't set up any logging if we're not sampling
            if not self.zipkin_attrs.is_sampled and not self.firehose_handler:
                return self
            # If transport is already configured don't override it. Doing so would
            # cause all previously recorded spans to never be emitted as exiting
            # the inner logging context will reset transport_configured to False.
            if self.get_tracer().is_transport_configured():
                log.info('Transport was already configured, ignoring override'
                         'from span {}'.format(self.span_name))
                return self
            endpoint = create_endpoint(self.port, self.service_name, self.host)
            self.logging_context = ZipkinLoggingContext(
                self.zipkin_attrs,
                endpoint,
                self.span_name,
                self.transport_handler,
                report_root_timestamp or self.report_root_timestamp_override,
                self.get_tracer,
                self.service_name,
                binary_annotations=self.binary_annotations,
                add_logging_annotation=self.add_logging_annotation,
                client_context=self.kind == Kind.CLIENT,
                max_span_batch_size=self.max_span_batch_size,
                firehose_handler=self.firehose_handler,
                encoding=self.encoding,
            )
            self.logging_context.start()
            self.get_tracer().set_transport_configured(configured=True)

        return self

Enter the new span context . All annotations logged inside this context will be attributed to this span . All new spans generated inside this context will have this span as their parent .


def stop(self, _exc_type=None, _exc_value=None, _exc_traceback=None):
        """Exit the span context. Zipkin attrs are pushed onto the
        threadlocal stack regardless of sampling, so they always need to be
        popped off. The actual logging of spans depends on sampling and that
        the logging was correctly set up.
        """

        if self.do_pop_attrs:
            self.get_tracer().pop_zipkin_attrs()

        # If no transport is configured, there's no reason to create a new Span.
        # This also helps avoiding memory leaks since without a transport nothing
        # would pull spans out of get_tracer().
        if not self.get_tracer().is_transport_configured():
            return

        # Add the error annotation if an exception occurred
        if any((_exc_type, _exc_value, _exc_traceback)):
            error_msg = u'{0}: {1}'.format(_exc_type.__name__, _exc_value)
            self.update_binary_annotations({
                ERROR_KEY: error_msg,
            })

        # Logging context is only initialized for "root" spans of the local
        # process (i.e. this zipkin_span not inside of any other local
        # zipkin_spans)
        if self.logging_context:
            try:
                self.logging_context.stop()
            except Exception as ex:
                err_msg = 'Error emitting zipkin trace. {}'.format(
                    repr(ex),
                )
                log.error(err_msg)
            finally:
                self.logging_context = None
                self.get_tracer().clear()
                self.get_tracer().set_transport_configured(configured=False)
                return

        # If we've gotten here, that means that this span is a child span of
        # this context's root span (i.e. it's a zipkin_span inside another
        # zipkin_span).
        end_timestamp = time.time()
        # If self.duration is set, it means the user wants to override it
        if self.duration:
            duration = self.duration
        else:
            duration = end_timestamp - self.start_timestamp

        endpoint = create_endpoint(self.port, self.service_name, self.host)
        self.get_tracer().add_span(Span(
            trace_id=self.zipkin_attrs.trace_id,
            name=self.span_name,
            parent_id=self.zipkin_attrs.parent_span_id,
            span_id=self.zipkin_attrs.span_id,
            kind=self.kind,
            timestamp=self.timestamp if self.timestamp else self.start_timestamp,
            duration=duration,
            annotations=self.annotations,
            local_endpoint=endpoint,
            remote_endpoint=self.remote_endpoint,
            tags=self.binary_annotations,
        ))

def
stop
(
self
,
_exc_type
=
,
_exc_value
=
,
_exc_traceback
=
)
:
if
self
.
do_pop_attrs
:
self
.
get_tracer
(
)
.
pop_zipkin_attrs
(
)
if
not
self
.
get_tracer
(
)
.
is_transport_configured
(
)
:
return
if
any
(
(
_exc_type
,
_exc_value
,
_exc_traceback
)
)
:
error_msg
=
u'{0}: {1}'
.
format
(
_exc_type
.
__name__
,
_exc_value
)
self
.
update_binary_annotations
(
{
ERROR_KEY
:
error_msg
,
}
)
if
self
.
logging_context
:
try
:
self
.
logging_context
.
stop
(
)
except
Exception
as
ex
:
err_msg
=
'Error emitting zipkin trace. {}'
.
format
(
repr
(
ex
)
,
)
log
.
error
(
err_msg
)
finally
:
self
.
logging_context
=
self
.
get_tracer
(
)
.
clear
(
)
self
.
get_tracer
(
)
.
set_transport_configured
(
configured
=
False
)
return
end_timestamp
=
time
.
time
(
)
if
self
.
duration
:
duration
=
self
.
duration
else
:
duration
=
end_timestamp
self
.
start_timestamp
endpoint
=
create_endpoint
(
self
.
port
,
self
.
service_name
,
self
.
host
)
self
.
get_tracer
(
)
.
add_span
(
Span
(
trace_id
=
self
.
zipkin_attrs
.
trace_id
,
name
=
self
.
span_name
,
parent_id
=
self
.
zipkin_attrs
.
parent_span_id
,
span_id
=
self
.
zipkin_attrs
.
span_id
,
kind
=
self
.
kind
,
timestamp
=
self
.
timestamp
if
self
.
timestamp
else
self
.
start_timestamp
,
duration
=
duration
,
annotations
=
self
.
annotations
,
local_endpoint
=
endpoint
,
remote_endpoint
=
self
.
remote_endpoint
,
tags
=
self
.
binary_annotations
,
)
)

Exit the span context . Zipkin attrs are pushed onto the threadlocal stack regardless of sampling so they always need to be popped off . The actual logging of spans depends on sampling and that the logging was correctly set up .


def update_binary_annotations(self, extra_annotations):
        """Updates the binary annotations for the current span."""
        if not self.logging_context:
            # This is not the root span, so binary annotations will be added
            # to the log handler when this span context exits.
            self.binary_annotations.update(extra_annotations)
        else:
            # Otherwise, we're in the context of the root span, so just update
            # the binary annotations for the logging context directly.
            self.logging_context.tags.update(extra_annotations)

def update_binary_annotations ( self , extra_annotations ) : if not self . logging_context : self . binary_annotations . update ( extra_annotations ) else : self . logging_context . tags . update ( extra_annotations )

Updates the binary annotations for the current span .


def add_sa_binary_annotation(
        self,
        port=0,
        service_name='unknown',
        host='127.0.0.1',
    ):
        """Adds a 'sa' binary annotation to the current span.

        'sa' binary annotations are useful for situations where you need to log
        where a request is going but the destination doesn't support zipkin.

        Note that the span must have 'cs'/'cr' annotations.

        :param port: The port number of the destination
        :type port: int
        :param service_name: The name of the destination service
        :type service_name: str
        :param host: Host address of the destination
        :type host: str
        """
        if self.kind != Kind.CLIENT:
            # TODO: trying to set a sa binary annotation for a non-client span
            # should result in a logged error
            return

        remote_endpoint = create_endpoint(
            port=port,
            service_name=service_name,
            host=host,
        )
        if not self.logging_context:
            if self.remote_endpoint is not None:
                raise ValueError('SA annotation already set.')
            self.remote_endpoint = remote_endpoint
        else:
            if self.logging_context.remote_endpoint is not None:
                raise ValueError('SA annotation already set.')
            self.logging_context.remote_endpoint = remote_endpoint

def
add_sa_binary_annotation
(
self
,
port
=
0
,
service_name
=
'unknown'
,
host
=
'127.0.0.1'
,
)
:
if
self
.
kind
!=
Kind
.
CLIENT
:
return
remote_endpoint
=
create_endpoint
(
port
=
port
,
service_name
=
service_name
,
host
=
host
,
)
if
not
self
.
logging_context
:
if
self
.
remote_endpoint
is
not
:
raise
ValueError
(
'SA annotation already set.'
)
self
.
remote_endpoint
=
remote_endpoint
else
:
if
self
.
logging_context
.
remote_endpoint
is
not
:
raise
ValueError
(
'SA annotation already set.'
)
self
.
logging_context
.
remote_endpoint
=
remote_endpoint

Adds a sa binary annotation to the current span .


def override_span_name(self, name):
        """Overrides the current span name.

        This is useful if you don't know the span name yet when you create the
        zipkin_span object. i.e. pyramid_zipkin doesn't know which route the
        request matched until the function wrapped by the context manager
        completes.

        :param name: New span name
        :type name: str
        """
        self.span_name = name
        if self.logging_context:
            self.logging_context.span_name = name

def override_span_name ( self , name ) : self . span_name = name if self . logging_context : self . logging_context . span_name = name


def create_endpoint(port=None, service_name=None, host=None, use_defaults=True):
    """Creates a new Endpoint object.

    :param port: TCP/UDP port. Defaults to 0.
    :type port: int
    :param service_name: service name as a str. Defaults to 'unknown'.
    :type service_name: str
    :param host: ipv4 or ipv6 address of the host. Defaults to the
    current host ip.
    :type host: str
    :param use_defaults: whether to use defaults.
    :type use_defaults: bool
    :returns: zipkin Endpoint object
    """
    if use_defaults:
        if port is None:
            port = 0
        if service_name is None:
            service_name = 'unknown'
        if host is None:
            try:
                host = socket.gethostbyname(socket.gethostname())
            except socket.gaierror:
                host = '127.0.0.1'

    ipv4 = None
    ipv6 = None

    if host:
        # Check ipv4 or ipv6.
        try:
            socket.inet_pton(socket.AF_INET, host)
            ipv4 = host
        except socket.error:
            # If it's not an ipv4 address, maybe it's ipv6.
            try:
                socket.inet_pton(socket.AF_INET6, host)
                ipv6 = host
            except socket.error:
                # If it's neither ipv4 or ipv6, leave both ip addresses unset.
                pass

    return Endpoint(
        ipv4=ipv4,
        ipv6=ipv6,
        port=port,
        service_name=service_name,
    )

def create_endpoint ( port = None , service_name = None , host = None , use_defaults = True ) : if use_defaults : if port is None : port = 0 if service_name is None : service_name = 'unknown' if host is None : try : host = socket . gethostbyname ( socket . gethostname ( ) ) except socket . gaierror : host = '127.0.0.1' ipv4 = None ipv6 = None if host : try : socket . inet_pton ( socket . AF_INET , host ) ipv4 = host except socket . error : try : socket . inet_pton ( socket . AF_INET6 , host ) ipv6 = host except socket . error : pass return Endpoint ( ipv4 = ipv4 , ipv6 = ipv6 , port = port , service_name = service_name , )


def copy_endpoint_with_new_service_name(endpoint, new_service_name):
    """Creates a copy of a given endpoint with a new service name.

    :param endpoint: existing Endpoint object
    :type endpoint: Endpoint
    :param new_service_name: new service name
    :type new_service_name: str
    :returns: zipkin new Endpoint object
    """
    return Endpoint(
        service_name=new_service_name,
        ipv4=endpoint.ipv4,
        ipv6=endpoint.ipv6,
        port=endpoint.port,
    )

def copy_endpoint_with_new_service_name ( endpoint , new_service_name ) : return Endpoint ( service_name = new_service_name , ipv4 = endpoint . ipv4 , ipv6 = endpoint . ipv6 , port = endpoint . port , )

Creates a copy of a given endpoint with a new service name .


def build_v1_span(self):
        """Builds and returns a V1 Span.

        :return: newly generated _V1Span
        :rtype: _V1Span
        """
        # We are simulating a full two-part span locally, so set cs=sr and ss=cr
        full_annotations = OrderedDict([
            ('cs', self.timestamp),
            ('sr', self.timestamp),
            ('ss', self.timestamp + self.duration),
            ('cr', self.timestamp + self.duration),
        ])

        if self.kind != Kind.LOCAL:
            # If kind is not LOCAL, then we only want client or
            # server side annotations.
            for ann in _DROP_ANNOTATIONS_BY_KIND[self.kind]:
                del full_annotations[ann]

        # Add user-defined annotations. We write them in full_annotations
        # instead of the opposite so that user annotations will override
        # any automatically generated annotation.
        full_annotations.update(self.annotations)

        return _V1Span(
            trace_id=self.trace_id,
            name=self.name,
            parent_id=self.parent_id,
            id=self.span_id,
            timestamp=self.timestamp if self.shared is False else None,
            duration=self.duration if self.shared is False else None,
            endpoint=self.local_endpoint,
            annotations=full_annotations,
            binary_annotations=self.tags,
            remote_endpoint=self.remote_endpoint,
        )

def build_v1_span ( self ) : full_annotations = OrderedDict ( [ ( 'cs' , self . timestamp ) , ( 'sr' , self . timestamp ) , ( 'ss' , self . timestamp + self . duration ) , ( 'cr' , self . timestamp + self . duration ) , ] ) if self . kind != Kind . LOCAL : for ann in _DROP_ANNOTATIONS_BY_KIND [ self . kind ] : del full_annotations [ ann ] full_annotations . update ( self . annotations ) return _V1Span ( trace_id = self . trace_id , name = self . name , parent_id = self . parent_id , id = self . span_id , timestamp = self . timestamp if self . shared is False else None , duration = self . duration if self . shared is False else None , endpoint = self . local_endpoint , annotations = full_annotations , binary_annotations = self . tags , remote_endpoint = self . remote_endpoint , )


def encode_pb_list(pb_spans):
    """Encode list of protobuf Spans to binary.

    :param pb_spans: list of protobuf Spans.
    :type pb_spans: list of zipkin_pb2.Span
    :return: encoded list.
    :rtype: bytes
    """
    pb_list = zipkin_pb2.ListOfSpans()
    pb_list.spans.extend(pb_spans)
    return pb_list.SerializeToString()

def encode_pb_list ( pb_spans ) : pb_list = zipkin_pb2 . ListOfSpans ( ) pb_list . spans . extend ( pb_spans ) return pb_list . SerializeToString ( )

Encode list of protobuf Spans to binary .


def create_protobuf_span(span):
    """Converts a py_zipkin Span in a protobuf Span.

    :param span: py_zipkin Span to convert.
    :type span: py_zipkin.encoding.Span
    :return: protobuf's Span
    :rtype: zipkin_pb2.Span
    """

    # Protobuf's composite types (i.e. Span's local_endpoint) are immutable.
    # So we can't create a zipkin_pb2.Span here and then set the appropriate
    # fields since `pb_span.local_endpoint = zipkin_pb2.Endpoint` fails.
    # Instead we just create the kwargs and pass them in to the Span constructor.
    pb_kwargs = {}

    pb_kwargs['trace_id'] = _hex_to_bytes(span.trace_id)

    if span.parent_id:
        pb_kwargs['parent_id'] = _hex_to_bytes(span.parent_id)

    pb_kwargs['id'] = _hex_to_bytes(span.span_id)

    pb_kind = _get_protobuf_kind(span.kind)
    if pb_kind:
        pb_kwargs['kind'] = pb_kind

    if span.name:
        pb_kwargs['name'] = span.name
    if span.timestamp:
        pb_kwargs['timestamp'] = int(span.timestamp * 1000 * 1000)
    if span.duration:
        pb_kwargs['duration'] = int(span.duration * 1000 * 1000)

    if span.local_endpoint:
        pb_kwargs['local_endpoint'] = _convert_endpoint(span.local_endpoint)

    if span.remote_endpoint:
        pb_kwargs['remote_endpoint'] = _convert_endpoint(span.remote_endpoint)

    if len(span.annotations) > 0:
        pb_kwargs['annotations'] = _convert_annotations(span.annotations)

    if len(span.tags) > 0:
        pb_kwargs['tags'] = span.tags

    if span.debug:
        pb_kwargs['debug'] = span.debug

    if span.shared:
        pb_kwargs['shared'] = span.shared

    return zipkin_pb2.Span(**pb_kwargs)

def create_protobuf_span ( span ) : pb_kwargs = { } pb_kwargs [ 'trace_id' ] = _hex_to_bytes ( span . trace_id ) if span . parent_id : pb_kwargs [ 'parent_id' ] = _hex_to_bytes ( span . parent_id ) pb_kwargs [ 'id' ] = _hex_to_bytes ( span . span_id ) pb_kind = _get_protobuf_kind ( span . kind ) if pb_kind : pb_kwargs [ 'kind' ] = pb_kind if span . name : pb_kwargs [ 'name' ] = span . name if span . timestamp : pb_kwargs [ 'timestamp' ] = int ( span . timestamp * 1000 * 1000 ) if span . duration : pb_kwargs [ 'duration' ] = int ( span . duration * 1000 * 1000 ) if span . local_endpoint : pb_kwargs [ 'local_endpoint' ] = _convert_endpoint ( span . local_endpoint ) if span . remote_endpoint : pb_kwargs [ 'remote_endpoint' ] = _convert_endpoint ( span . remote_endpoint ) if len ( span . annotations ) > 0 : pb_kwargs [ 'annotations' ] = _convert_annotations ( span . annotations ) if len ( span . tags ) > 0 : pb_kwargs [ 'tags' ] = span . tags if span . debug : pb_kwargs [ 'debug' ] = span . debug if span . shared : pb_kwargs [ 'shared' ] = span . shared return zipkin_pb2 . Span ( ** pb_kwargs )

Converts a py_zipkin Span in a protobuf Span .


def _hex_to_bytes(hex_id):
    """Encodes to hexadecimal ids to big-endian binary.

    :param hex_id: hexadecimal id to encode.
    :type hex_id: str
    :return: binary representation.
    :type: bytes
    """
    if len(hex_id) <= 16:
        int_id = unsigned_hex_to_signed_int(hex_id)
        return struct.pack('>q', int_id)
    else:
        # There's no 16-bytes encoding in Python's struct. So we convert the
        # id as 2 64 bit ids and then concatenate the result.

        # NOTE: we count 16 chars from the right (:-16) rather than the left so
        # that ids with less than 32 chars will be correctly pre-padded with 0s.
        high_id = unsigned_hex_to_signed_int(hex_id[:-16])
        high_bin = struct.pack('>q', high_id)

        low_id = unsigned_hex_to_signed_int(hex_id[-16:])
        low_bin = struct.pack('>q', low_id)

        return high_bin + low_bin

def _hex_to_bytes ( hex_id ) : if len ( hex_id ) <= 16 : int_id = unsigned_hex_to_signed_int ( hex_id ) return struct . pack ( '>q' , int_id ) else : high_id = unsigned_hex_to_signed_int ( hex_id [ : - 16 ] ) high_bin = struct . pack ( '>q' , high_id ) low_id = unsigned_hex_to_signed_int ( hex_id [ - 16 : ] ) low_bin = struct . pack ( '>q' , low_id ) return high_bin + low_bin

Encodes to hexadecimal ids to big - endian binary .


def _get_protobuf_kind(kind):
    """Converts py_zipkin's Kind to Protobuf's Kind.

    :param kind: py_zipkin's Kind.
    :type kind: py_zipkin.Kind
    :return: correcponding protobuf's kind value.
    :rtype: zipkin_pb2.Span.Kind
    """
    if kind == Kind.CLIENT:
        return zipkin_pb2.Span.CLIENT
    elif kind == Kind.SERVER:
        return zipkin_pb2.Span.SERVER
    elif kind == Kind.PRODUCER:
        return zipkin_pb2.Span.PRODUCER
    elif kind == Kind.CONSUMER:
        return zipkin_pb2.Span.CONSUMER
    return None

def _get_protobuf_kind ( kind ) : if kind == Kind . CLIENT : return zipkin_pb2 . Span . CLIENT elif kind == Kind . SERVER : return zipkin_pb2 . Span . SERVER elif kind == Kind . PRODUCER : return zipkin_pb2 . Span . PRODUCER elif kind == Kind . CONSUMER : return zipkin_pb2 . Span . CONSUMER return None

Converts py_zipkin s Kind to Protobuf s Kind .


def _convert_endpoint(endpoint):
    """Converts py_zipkin's Endpoint to Protobuf's Endpoint.

    :param endpoint: py_zipkins' endpoint to convert.
    :type endpoint: py_zipkin.encoding.Endpoint
    :return: corresponding protobuf's endpoint.
    :rtype: zipkin_pb2.Endpoint
    """
    pb_endpoint = zipkin_pb2.Endpoint()

    if endpoint.service_name:
        pb_endpoint.service_name = endpoint.service_name
    if endpoint.port and endpoint.port != 0:
        pb_endpoint.port = endpoint.port
    if endpoint.ipv4:
        pb_endpoint.ipv4 = socket.inet_pton(socket.AF_INET, endpoint.ipv4)
    if endpoint.ipv6:
        pb_endpoint.ipv6 = socket.inet_pton(socket.AF_INET6, endpoint.ipv6)

    return pb_endpoint

def _convert_endpoint ( endpoint ) : pb_endpoint = zipkin_pb2 . Endpoint ( ) if endpoint . service_name : pb_endpoint . service_name = endpoint . service_name if endpoint . port and endpoint . port != 0 : pb_endpoint . port = endpoint . port if endpoint . ipv4 : pb_endpoint . ipv4 = socket . inet_pton ( socket . AF_INET , endpoint . ipv4 ) if endpoint . ipv6 : pb_endpoint . ipv6 = socket . inet_pton ( socket . AF_INET6 , endpoint . ipv6 ) return pb_endpoint

Converts py_zipkin s Endpoint to Protobuf s Endpoint .


def _convert_annotations(annotations):
    """Converts py_zipkin's annotations dict to protobuf.

    :param annotations: annotations dict.
    :type annotations: dict
    :return: corresponding protobuf's list of annotations.
    :rtype: list
    """
    pb_annotations = []
    for value, ts in annotations.items():
        pb_annotations.append(zipkin_pb2.Annotation(
            timestamp=int(ts * 1000 * 1000),
            value=value,
        ))
    return pb_annotations

def _convert_annotations ( annotations ) : pb_annotations = [ ] for value , ts in annotations . items ( ) : pb_annotations . append ( zipkin_pb2 . Annotation ( timestamp = int ( ts * 1000 * 1000 ) , value = value , ) ) return pb_annotations

Converts py_zipkin s annotations dict to protobuf .


def create_annotation(timestamp, value, host):
    """
    Create a zipkin annotation object

    :param timestamp: timestamp of when the annotation occured in microseconds
    :param value: name of the annotation, such as 'sr'
    :param host: zipkin endpoint object

    :returns: zipkin annotation object
    """
    return zipkin_core.Annotation(timestamp=timestamp, value=value, host=host)

def create_annotation ( timestamp , value , host ) : return zipkin_core . Annotation ( timestamp = timestamp , value = value , host = host )


def create_binary_annotation(key, value, annotation_type, host):
    """
    Create a zipkin binary annotation object

    :param key: name of the annotation, such as 'http.uri'
    :param value: value of the annotation, such as a URI
    :param annotation_type: type of annotation, such as AnnotationType.I32
    :param host: zipkin endpoint object

    :returns: zipkin binary annotation object
    """
    return zipkin_core.BinaryAnnotation(
        key=key,
        value=value,
        annotation_type=annotation_type,
        host=host,
    )

def create_binary_annotation ( key , value , annotation_type , host ) : return zipkin_core . BinaryAnnotation ( key = key , value = value , annotation_type = annotation_type , host = host , )


def create_endpoint(port=0, service_name='unknown', ipv4=None, ipv6=None):
    """Create a zipkin Endpoint object.

    An Endpoint object holds information about the network context of a span.

    :param port: int value of the port. Defaults to 0
    :param service_name: service name as a str. Defaults to 'unknown'
    :param ipv4: ipv4 host address
    :param ipv6: ipv6 host address
    :returns: thrift Endpoint object
    """
    ipv4_int = 0
    ipv6_binary = None

    # Convert ip address to network byte order
    if ipv4:
        ipv4_int = struct.unpack('!i', socket.inet_pton(socket.AF_INET, ipv4))[0]

    if ipv6:
        ipv6_binary = socket.inet_pton(socket.AF_INET6, ipv6)

    # Zipkin passes unsigned values in signed types because Thrift has no
    # unsigned types, so we have to convert the value.
    port = struct.unpack('h', struct.pack('H', port))[0]
    return zipkin_core.Endpoint(
        ipv4=ipv4_int,
        ipv6=ipv6_binary,
        port=port,
        service_name=service_name,
    )

def create_endpoint ( port = 0 , service_name = 'unknown' , ipv4 = None , ipv6 = None ) : ipv4_int = 0 ipv6_binary = None if ipv4 : ipv4_int = struct . unpack ( '!i' , socket . inet_pton ( socket . AF_INET , ipv4 ) ) [ 0 ] if ipv6 : ipv6_binary = socket . inet_pton ( socket . AF_INET6 , ipv6 ) port = struct . unpack ( 'h' , struct . pack ( 'H' , port ) ) [ 0 ] return zipkin_core . Endpoint ( ipv4 = ipv4_int , ipv6 = ipv6_binary , port = port , service_name = service_name , )


def copy_endpoint_with_new_service_name(endpoint, service_name):
    """Copies a copy of a given endpoint with a new service name.
    This should be very fast, on the order of several microseconds.

    :param endpoint: existing zipkin_core.Endpoint object
    :param service_name: str of new service name
    :returns: zipkin Endpoint object
    """
    return zipkin_core.Endpoint(
        ipv4=endpoint.ipv4,
        port=endpoint.port,
        service_name=service_name,
    )

def copy_endpoint_with_new_service_name ( endpoint , service_name ) : return zipkin_core . Endpoint ( ipv4 = endpoint . ipv4 , port = endpoint . port , service_name = service_name , )

Copies a copy of a given endpoint with a new service name . This should be very fast on the order of several microseconds .


def annotation_list_builder(annotations, host):
    """
    Reformat annotations dict to return list of corresponding zipkin_core objects.

    :param annotations: dict containing key as annotation name,
                        value being timestamp in seconds(float).
    :type host: :class:`zipkin_core.Endpoint`
    :returns: a list of annotation zipkin_core objects
    :rtype: list
    """
    return [
        create_annotation(int(timestamp * 1000000), key, host)
        for key, timestamp in annotations.items()
    ]

def annotation_list_builder ( annotations , host ) : return [ create_annotation ( int ( timestamp * 1000000 ) , key , host ) for key , timestamp in annotations . items ( ) ]

Reformat annotations dict to return list of corresponding zipkin_core objects .


def binary_annotation_list_builder(binary_annotations, host):
    """
    Reformat binary annotations dict to return list of zipkin_core objects. The
    value of the binary annotations MUST be in string format.

    :param binary_annotations: dict with key, value being the name and value
                               of the binary annotation being logged.
    :type host: :class:`zipkin_core.Endpoint`
    :returns: a list of binary annotation zipkin_core objects
    :rtype: list
    """
    # TODO: Remove the type hard-coding of STRING to take it as a param option.
    ann_type = zipkin_core.AnnotationType.STRING
    return [
        create_binary_annotation(key, str(value), ann_type, host)
        for key, value in binary_annotations.items()
    ]

def binary_annotation_list_builder ( binary_annotations , host ) : ann_type = zipkin_core . AnnotationType . STRING return [ create_binary_annotation ( key , str ( value ) , ann_type , host ) for key , value in binary_annotations . items ( ) ]

Reformat binary annotations dict to return list of zipkin_core objects . The value of the binary annotations MUST be in string format .


def create_span(
    span_id,
    parent_span_id,
    trace_id,
    span_name,
    annotations,
    binary_annotations,
    timestamp_s,
    duration_s,
):
    """Takes a bunch of span attributes and returns a thriftpy2 representation
    of the span. Timestamps passed in are in seconds, they're converted to
    microseconds before thrift encoding.
    """
    # Check if trace_id is 128-bit. If so, record trace_id_high separately.
    trace_id_length = len(trace_id)
    trace_id_high = None
    if trace_id_length > 16:
        assert trace_id_length == 32
        trace_id, trace_id_high = trace_id[16:], trace_id[:16]

    if trace_id_high:
        trace_id_high = unsigned_hex_to_signed_int(trace_id_high)

    span_dict = {
        'trace_id': unsigned_hex_to_signed_int(trace_id),
        'name': span_name,
        'id': unsigned_hex_to_signed_int(span_id),
        'annotations': annotations,
        'binary_annotations': binary_annotations,
        'timestamp': int(timestamp_s * 1000000) if timestamp_s else None,
        'duration': int(duration_s * 1000000) if duration_s else None,
        'trace_id_high': trace_id_high,
    }
    if parent_span_id:
        span_dict['parent_id'] = unsigned_hex_to_signed_int(parent_span_id)
    return zipkin_core.Span(**span_dict)

Takes a bunch of span attributes and returns a thriftpy2 representation of the span . Timestamps passed in are in seconds they re converted to microseconds before thrift encoding .


def span_to_bytes(thrift_span):
    """
    Returns a TBinaryProtocol encoded Thrift span.

    :param thrift_span: thrift object to encode.
    :returns: thrift object in TBinaryProtocol format bytes.
    """
    transport = TMemoryBuffer()
    protocol = TBinaryProtocol(transport)
    thrift_span.write(protocol)

    return bytes(transport.getvalue())

def span_to_bytes ( thrift_span ) : transport = TMemoryBuffer ( ) protocol = TBinaryProtocol ( transport ) thrift_span . write ( protocol ) return bytes ( transport . getvalue ( ) )

Returns a TBinaryProtocol encoded Thrift span .


def encode_bytes_list(binary_thrift_obj_list):  # pragma: no cover
    """
    Returns a TBinaryProtocol encoded list of Thrift objects.

    :param binary_thrift_obj_list: list of TBinaryProtocol objects to encode.
    :returns: bynary object representing the encoded list.
    """
    transport = TMemoryBuffer()
    write_list_begin(transport, TType.STRUCT, len(binary_thrift_obj_list))
    for thrift_bin in binary_thrift_obj_list:
        transport.write(thrift_bin)

    return bytes(transport.getvalue())

def encode_bytes_list ( binary_thrift_obj_list ) : transport = TMemoryBuffer ( ) write_list_begin ( transport , TType . STRUCT , len ( binary_thrift_obj_list ) ) for thrift_bin in binary_thrift_obj_list : transport . write ( thrift_bin ) return bytes ( transport . getvalue ( ) )

Returns a TBinaryProtocol encoded list of Thrift objects .


def detect_span_version_and_encoding(message):
    """Returns the span type and encoding for the message provided.

    The logic in this function is a Python port of
    https://github.com/openzipkin/zipkin/blob/master/zipkin/src/main/java/zipkin/internal/DetectingSpanDecoder.java

    :param message: span to perform operations on.
    :type message: byte array
    :returns: span encoding.
    :rtype: Encoding
    """
    # In case message is sent in as non-bytearray format,
    # safeguard convert to bytearray before handling
    if isinstance(message, six.string_types):
        # Even six.b is not enough to handle the py2/3 difference since
        # it uses latin-1 as default encoding and not utf-8.
        if six.PY2:
            message = six.b(message)  # pragma: no cover
        else:
            message = message.encode('utf-8')  # pragma: no cover

    if len(message) < 2:
        raise ZipkinError("Invalid span format. Message too short.")

    # Check for binary format
    if six.byte2int(message) <= 16:
        if six.byte2int(message) == 10 and six.byte2int(message[1:2]) != 0:
            return Encoding.V2_PROTO3
        return Encoding.V1_THRIFT

    str_msg = message.decode('utf-8')

    # JSON case for list of spans
    if str_msg[0] == '[':
        span_list = json.loads(str_msg)
        if len(span_list) > 0:
            # Assumption: All spans in a list are the same version
            # Logic: Search for identifying fields in all spans, if any span can
            # be strictly identified to a version, return that version.
            # Otherwise, if no spans could be strictly identified, default to V2.
            for span in span_list:
                if any(word in span for word in _V2_ATTRIBUTES):
                    return Encoding.V2_JSON
                elif (
                    'binaryAnnotations' in span or
                    (
                        'annotations' in span and
                        'endpoint' in span['annotations']
                    )
                ):
                    return Encoding.V1_JSON
            return Encoding.V2_JSON

    raise ZipkinError("Unknown or unsupported span encoding")

Returns the span type and encoding for the message provided .


def convert_spans(spans, output_encoding, input_encoding=None):
    """Converts encoded spans to a different encoding.

    param spans: encoded input spans.
    type spans: byte array
    param output_encoding: desired output encoding.
    type output_encoding: Encoding
    param input_encoding: optional input encoding. If this is not specified, it'll
        try to understand the encoding automatically by inspecting the input spans.
    type input_encoding: Encoding
    :returns: encoded spans.
    :rtype: byte array
    """
    if not isinstance(input_encoding, Encoding):
        input_encoding = detect_span_version_and_encoding(message=spans)

    if input_encoding == output_encoding:
        return spans

    decoder = get_decoder(input_encoding)
    encoder = get_encoder(output_encoding)
    decoded_spans = decoder.decode_spans(spans)
    output_spans = []

    # Encode each indivicual span
    for span in decoded_spans:
        output_spans.append(encoder.encode_span(span))

    # Outputs from encoder.encode_span() can be easily concatenated in a list
    return encoder.encode_queue(output_spans)

def convert_spans ( spans , output_encoding , input_encoding = None ) : if not isinstance ( input_encoding , Encoding ) : input_encoding = detect_span_version_and_encoding ( message = spans ) if input_encoding == output_encoding : return spans decoder = get_decoder ( input_encoding ) encoder = get_encoder ( output_encoding ) decoded_spans = decoder . decode_spans ( spans ) output_spans = [ ] for span in decoded_spans : output_spans . append ( encoder . encode_span ( span ) ) return encoder . encode_queue ( output_spans )

Converts encoded spans to a different encoding .


def push_zipkin_attrs(zipkin_attr):
    """Stores the zipkin attributes to thread local.

    .. deprecated::
       Use the Tracer interface which offers better multi-threading support.
       push_zipkin_attrs will be removed in version 1.0.

    :param zipkin_attr: tuple containing zipkin related attrs
    :type zipkin_attr: :class:`zipkin.ZipkinAttrs`
    """
    from py_zipkin.storage import ThreadLocalStack
    log.warning('push_zipkin_attrs is deprecated. See DEPRECATIONS.rst for'
                'details on how to migrate to using Tracer.')
    return ThreadLocalStack().push(zipkin_attr)

Stores the zipkin attributes to thread local .


def encode_span(self, v2_span):
        """Encodes the current span to thrift."""
        span = v2_span.build_v1_span()

        thrift_endpoint = thrift.create_endpoint(
            span.endpoint.port,
            span.endpoint.service_name,
            span.endpoint.ipv4,
            span.endpoint.ipv6,
        )

        thrift_annotations = thrift.annotation_list_builder(
            span.annotations,
            thrift_endpoint,
        )

        thrift_binary_annotations = thrift.binary_annotation_list_builder(
            span.binary_annotations,
            thrift_endpoint,
        )

        # Add sa/ca binary annotations
        if v2_span.remote_endpoint:
            self.encode_remote_endpoint(
                v2_span.remote_endpoint,
                v2_span.kind,
                thrift_binary_annotations,
            )

        thrift_span = thrift.create_span(
            span.id,
            span.parent_id,
            span.trace_id,
            span.name,
            thrift_annotations,
            thrift_binary_annotations,
            span.timestamp,
            span.duration,
        )

        encoded_span = thrift.span_to_bytes(thrift_span)
        return encoded_span

def encode_span ( self , v2_span ) : span = v2_span . build_v1_span ( ) thrift_endpoint = thrift . create_endpoint ( span . endpoint . port , span . endpoint . service_name , span . endpoint . ipv4 , span . endpoint . ipv6 , ) thrift_annotations = thrift . annotation_list_builder ( span . annotations , thrift_endpoint , ) thrift_binary_annotations = thrift . binary_annotation_list_builder ( span . binary_annotations , thrift_endpoint , ) if v2_span . remote_endpoint : self . encode_remote_endpoint ( v2_span . remote_endpoint , v2_span . kind , thrift_binary_annotations , ) thrift_span = thrift . create_span ( span . id , span . parent_id , span . trace_id , span . name , thrift_annotations , thrift_binary_annotations , span . timestamp , span . duration , ) encoded_span = thrift . span_to_bytes ( thrift_span ) return encoded_span


def _create_json_endpoint(self, endpoint, is_v1):
        """Converts an Endpoint to a JSON endpoint dict.

        :param endpoint: endpoint object to convert.
        :type endpoint: Endpoint
        :param is_v1: whether we're serializing a v1 span. This is needed since
            in v1 some fields default to an empty string rather than being
            dropped if they're not set.
        :type is_v1: bool
        :return: dict representing a JSON endpoint.
        :rtype: dict
        """
        json_endpoint = {}

        if endpoint.service_name:
            json_endpoint['serviceName'] = endpoint.service_name
        elif is_v1:
            # serviceName is mandatory in v1
            json_endpoint['serviceName'] = ""
        if endpoint.port and endpoint.port != 0:
            json_endpoint['port'] = endpoint.port
        if endpoint.ipv4 is not None:
            json_endpoint['ipv4'] = endpoint.ipv4
        if endpoint.ipv6 is not None:
            json_endpoint['ipv6'] = endpoint.ipv6

        return json_endpoint

def _create_json_endpoint ( self , endpoint , is_v1 ) : json_endpoint = { } if endpoint . service_name : json_endpoint [ 'serviceName' ] = endpoint . service_name elif is_v1 : json_endpoint [ 'serviceName' ] = "" if endpoint . port and endpoint . port != 0 : json_endpoint [ 'port' ] = endpoint . port if endpoint . ipv4 is not None : json_endpoint [ 'ipv4' ] = endpoint . ipv4 if endpoint . ipv6 is not None : json_endpoint [ 'ipv6' ] = endpoint . ipv6 return json_endpoint

Converts an Endpoint to a JSON endpoint dict .


def encode_span(self, span):
        """Encodes a single span to protobuf."""
        if not protobuf.installed():
            raise ZipkinError(
                'protobuf encoding requires installing the protobuf\'s extra '
                'requirements. Use py-zipkin[protobuf] in your requirements.txt.'
            )

        pb_span = protobuf.create_protobuf_span(span)
        return protobuf.encode_pb_list([pb_span])


def decode_spans(self, spans):
        """Decodes an encoded list of spans.

        :param spans: encoded list of spans
        :type spans: bytes
        :return: list of spans
        :rtype: list of Span
        """
        decoded_spans = []
        transport = TMemoryBuffer(spans)

        if six.byte2int(spans) == TType.STRUCT:
            _, size = read_list_begin(transport)
        else:
            size = 1

        for _ in range(size):
            span = zipkin_core.Span()
            span.read(TBinaryProtocol(transport))
            decoded_spans.append(self._decode_thrift_span(span))
        return decoded_spans

def decode_spans ( self , spans ) : decoded_spans = [ ] transport = TMemoryBuffer ( spans ) if six . byte2int ( spans ) == TType . STRUCT : _ , size = read_list_begin ( transport ) else : size = 1 for _ in range ( size ) : span = zipkin_core . Span ( ) span . read ( TBinaryProtocol ( transport ) ) decoded_spans . append ( self . _decode_thrift_span ( span ) ) return decoded_spans


def _convert_from_thrift_endpoint(self, thrift_endpoint):
        """Accepts a thrift decoded endpoint and converts it to an Endpoint.

        :param thrift_endpoint: thrift encoded endpoint
        :type thrift_endpoint: thrift endpoint
        :returns: decoded endpoint
        :rtype: Encoding
        """
        ipv4 = None
        ipv6 = None
        port = struct.unpack('H', struct.pack('h', thrift_endpoint.port))[0]

        if thrift_endpoint.ipv4 != 0:
            ipv4 = socket.inet_ntop(
                socket.AF_INET,
                struct.pack('!i', thrift_endpoint.ipv4),
            )

        if thrift_endpoint.ipv6:
            ipv6 = socket.inet_ntop(socket.AF_INET6, thrift_endpoint.ipv6)

        return Endpoint(
            service_name=thrift_endpoint.service_name,
            ipv4=ipv4,
            ipv6=ipv6,
            port=port,
        )

def _convert_from_thrift_endpoint ( self , thrift_endpoint ) : ipv4 = None ipv6 = None port = struct . unpack ( 'H' , struct . pack ( 'h' , thrift_endpoint . port ) ) [ 0 ] if thrift_endpoint . ipv4 != 0 : ipv4 = socket . inet_ntop ( socket . AF_INET , struct . pack ( '!i' , thrift_endpoint . ipv4 ) , ) if thrift_endpoint . ipv6 : ipv6 = socket . inet_ntop ( socket . AF_INET6 , thrift_endpoint . ipv6 ) return Endpoint ( service_name = thrift_endpoint . service_name , ipv4 = ipv4 , ipv6 = ipv6 , port = port , )

Accepts a thrift decoded endpoint and converts it to an Endpoint .


def _decode_thrift_annotations(self, thrift_annotations):
        """Accepts a thrift annotation and converts it to a v1 annotation.

        :param thrift_annotations: list of thrift annotations.
        :type thrift_annotations: list of zipkin_core.Span.Annotation
        :returns: (annotations, local_endpoint, kind)
        """
        local_endpoint = None
        kind = Kind.LOCAL
        all_annotations = {}
        timestamp = None
        duration = None

        for thrift_annotation in thrift_annotations:
            all_annotations[thrift_annotation.value] = thrift_annotation.timestamp
            if thrift_annotation.host:
                local_endpoint = self._convert_from_thrift_endpoint(
                    thrift_annotation.host,
                )

        if 'cs' in all_annotations and 'sr' not in all_annotations:
            kind = Kind.CLIENT
            timestamp = all_annotations['cs']
            duration = all_annotations['cr'] - all_annotations['cs']
        elif 'cs' not in all_annotations and 'sr' in all_annotations:
            kind = Kind.SERVER
            timestamp = all_annotations['sr']
            duration = all_annotations['ss'] - all_annotations['sr']

        annotations = {
            name: self.seconds(ts) for name, ts in all_annotations.items()
            if name not in _DROP_ANNOTATIONS
        }

        return annotations, local_endpoint, kind, timestamp, duration

def _decode_thrift_annotations ( self , thrift_annotations ) : local_endpoint = None kind = Kind . LOCAL all_annotations = { } timestamp = None duration = None for thrift_annotation in thrift_annotations : all_annotations [ thrift_annotation . value ] = thrift_annotation . timestamp if thrift_annotation . host : local_endpoint = self . _convert_from_thrift_endpoint ( thrift_annotation . host , ) if 'cs' in all_annotations and 'sr' not in all_annotations : kind = Kind . CLIENT timestamp = all_annotations [ 'cs' ] duration = all_annotations [ 'cr' ] - all_annotations [ 'cs' ] elif 'cs' not in all_annotations and 'sr' in all_annotations : kind = Kind . SERVER timestamp = all_annotations [ 'sr' ] duration = all_annotations [ 'ss' ] - all_annotations [ 'sr' ] annotations = { name : self . seconds ( ts ) for name , ts in all_annotations . items ( ) if name not in _DROP_ANNOTATIONS } return annotations , local_endpoint , kind , timestamp , duration

Accepts a thrift annotation and converts it to a v1 annotation .


def _convert_from_thrift_binary_annotations(self, thrift_binary_annotations):
        """Accepts a thrift decoded binary annotation and converts it
        to a v1 binary annotation.
        """
        tags = {}
        local_endpoint = None
        remote_endpoint = None

        for binary_annotation in thrift_binary_annotations:
            if binary_annotation.key == 'sa':
                remote_endpoint = self._convert_from_thrift_endpoint(
                    thrift_endpoint=binary_annotation.host,
                )
            else:
                key = binary_annotation.key

                annotation_type = binary_annotation.annotation_type
                value = binary_annotation.value

                if annotation_type == zipkin_core.AnnotationType.BOOL:
                    tags[key] = "true" if value == 1 else "false"
                elif annotation_type == zipkin_core.AnnotationType.STRING:
                    tags[key] = str(value)
                else:
                    log.warning('Only STRING and BOOL binary annotations are '
                                'supported right now and can be properly decoded.')

                if binary_annotation.host:
                    local_endpoint = self._convert_from_thrift_endpoint(
                        thrift_endpoint=binary_annotation.host,
                    )

        return tags, local_endpoint, remote_endpoint

Accepts a thrift decoded binary annotation and converts it to a v1 binary annotation .


def _decode_thrift_span(self, thrift_span):
        """Decodes a thrift span.

        :param thrift_span: thrift span
        :type thrift_span: thrift Span object
        :returns: span builder representing this span
        :rtype: Span
        """
        parent_id = None
        local_endpoint = None
        annotations = {}
        tags = {}
        kind = Kind.LOCAL
        remote_endpoint = None
        timestamp = None
        duration = None

        if thrift_span.parent_id:
            parent_id = self._convert_unsigned_long_to_lower_hex(
                thrift_span.parent_id,
            )

        if thrift_span.annotations:
            annotations, local_endpoint, kind, timestamp, duration = \
                self._decode_thrift_annotations(thrift_span.annotations)

        if thrift_span.binary_annotations:
            tags, local_endpoint, remote_endpoint = \
                self._convert_from_thrift_binary_annotations(
                    thrift_span.binary_annotations,
                )

        trace_id = self._convert_trace_id_to_string(
            thrift_span.trace_id,
            thrift_span.trace_id_high,
        )

        return Span(
            trace_id=trace_id,
            name=thrift_span.name,
            parent_id=parent_id,
            span_id=self._convert_unsigned_long_to_lower_hex(thrift_span.id),
            kind=kind,
            timestamp=self.seconds(timestamp or thrift_span.timestamp),
            duration=self.seconds(duration or thrift_span.duration),
            local_endpoint=local_endpoint,
            remote_endpoint=remote_endpoint,
            shared=(kind == Kind.SERVER and thrift_span.timestamp is None),
            annotations=annotations,
            tags=tags,
        )

def _decode_thrift_span ( self , thrift_span ) : parent_id = None local_endpoint = None annotations = { } tags = { } kind = Kind . LOCAL remote_endpoint = None timestamp = None duration = None if thrift_span . parent_id : parent_id = self . _convert_unsigned_long_to_lower_hex ( thrift_span . parent_id , ) if thrift_span . annotations : annotations , local_endpoint , kind , timestamp , duration = self . _decode_thrift_annotations ( thrift_span . annotations ) if thrift_span . binary_annotations : tags , local_endpoint , remote_endpoint = self . _convert_from_thrift_binary_annotations ( thrift_span . binary_annotations , ) trace_id = self . _convert_trace_id_to_string ( thrift_span . trace_id , thrift_span . trace_id_high , ) return Span ( trace_id = trace_id , name = thrift_span . name , parent_id = parent_id , span_id = self . _convert_unsigned_long_to_lower_hex ( thrift_span . id ) , kind = kind , timestamp = self . seconds ( timestamp or thrift_span . timestamp ) , duration = self . seconds ( duration or thrift_span . duration ) , local_endpoint = local_endpoint , remote_endpoint = remote_endpoint , shared = ( kind == Kind . SERVER and thrift_span . timestamp is None ) , annotations = annotations , tags = tags , )


def _convert_trace_id_to_string(self, trace_id, trace_id_high=None):
        """
        Converts the provided traceId hex value with optional high bits
        to a string.

        :param trace_id: the value of the trace ID
        :type trace_id: int
        :param trace_id_high: the high bits of the trace ID
        :type trace_id: int
        :returns: trace_id_high + trace_id as a string
        """
        if trace_id_high is not None:
            result = bytearray(32)
            self._write_hex_long(result, 0, trace_id_high)
            self._write_hex_long(result, 16, trace_id)
            return result.decode("utf8")

        result = bytearray(16)
        self._write_hex_long(result, 0, trace_id)
        return result.decode("utf8")

def _convert_trace_id_to_string ( self , trace_id , trace_id_high = None ) : if trace_id_high is not None : result = bytearray ( 32 ) self . _write_hex_long ( result , 0 , trace_id_high ) self . _write_hex_long ( result , 16 , trace_id ) return result . decode ( "utf8" ) result = bytearray ( 16 ) self . _write_hex_long ( result , 0 , trace_id ) return result . decode ( "utf8" )

Converts the provided traceId hex value with optional high bits to a string .


def _convert_unsigned_long_to_lower_hex(self, value):
        """
        Converts the provided unsigned long value to a hex string.

        :param value: the value to convert
        :type value: unsigned long
        :returns: value as a hex string
        """
        result = bytearray(16)
        self._write_hex_long(result, 0, value)
        return result.decode("utf8")

def _convert_unsigned_long_to_lower_hex ( self , value ) : result = bytearray ( 16 ) self . _write_hex_long ( result , 0 , value ) return result . decode ( "utf8" )

Converts the provided unsigned long value to a hex string .


def _write_hex_long(self, data, pos, value):
        """
        Writes an unsigned long value across a byte array.

        :param data: the buffer to write the value to
        :type data: bytearray
        :param pos: the starting position
        :type pos: int
        :param value: the value to write
        :type value: unsigned long
        """
        self._write_hex_byte(data, pos + 0, (value >> 56) & 0xff)
        self._write_hex_byte(data, pos + 2, (value >> 48) & 0xff)
        self._write_hex_byte(data, pos + 4, (value >> 40) & 0xff)
        self._write_hex_byte(data, pos + 6, (value >> 32) & 0xff)
        self._write_hex_byte(data, pos + 8, (value >> 24) & 0xff)
        self._write_hex_byte(data, pos + 10, (value >> 16) & 0xff)
        self._write_hex_byte(data, pos + 12, (value >> 8) & 0xff)
        self._write_hex_byte(data, pos + 14, (value & 0xff))

def _write_hex_long ( self , data , pos , value ) : self . _write_hex_byte ( data , pos + 0 , ( value >> 56 ) & 0xff ) self . _write_hex_byte ( data , pos + 2 , ( value >> 48 ) & 0xff ) self . _write_hex_byte ( data , pos + 4 , ( value >> 40 ) & 0xff ) self . _write_hex_byte ( data , pos + 6 , ( value >> 32 ) & 0xff ) self . _write_hex_byte ( data , pos + 8 , ( value >> 24 ) & 0xff ) self . _write_hex_byte ( data , pos + 10 , ( value >> 16 ) & 0xff ) self . _write_hex_byte ( data , pos + 12 , ( value >> 8 ) & 0xff ) self . _write_hex_byte ( data , pos + 14 , ( value & 0xff ) )

Writes an unsigned long value across a byte array .


def date_fixup_pre_processor(transactions, tag, tag_dict, *args):
    """
    Replace illegal February 29, 30 dates with the last day of February.

    German banks use a variant of the 30/360 interest rate calculation,
    where each month has always 30 days even February. Python's datetime
    module won't accept such dates.
    """
    if tag_dict['month'] == '02':
        year = int(tag_dict['year'], 10)
        _, max_month_day = calendar.monthrange(year, 2)
        if int(tag_dict['day'], 10) > max_month_day:
            tag_dict['day'] = str(max_month_day)

    return tag_dict

def date_fixup_pre_processor ( transactions , tag , tag_dict , * args ) : if tag_dict [ 'month' ] == '02' : year = int ( tag_dict [ 'year' ] , 10 ) _ , max_month_day = calendar . monthrange ( year , 2 ) if int ( tag_dict [ 'day' ] , 10 ) > max_month_day : tag_dict [ 'day' ] = str ( max_month_day ) return tag_dict

Replace illegal February 29 30 dates with the last day of February .


def mBank_set_transaction_code(transactions, tag, tag_dict, *args):
    """
    mBank Collect uses transaction code 911 to distinguish icoming mass
    payments transactions, adding transaction_code may be helpful in further
    processing
    """
    tag_dict['transaction_code'] = int(
        tag_dict[tag.slug].split(';')[0].split(' ', 1)[0])

    return tag_dict

def
mBank_set_transaction_code
(
transactions
,
tag
,
tag_dict
,
*
args
)
:
tag_dict
[
'transaction_code'
]
=
int
(
tag_dict
[
tag
.
slug
]
.
split
(
';'
)
[
0
]
.
split
(
' '
,
1
)
[
0
]
)
return
tag_dict

mBank Collect uses transaction code 911 to distinguish icoming mass payments transactions adding transaction_code may be helpful in further processing


def mBank_set_iph_id(transactions, tag, tag_dict, *args):
    """
    mBank Collect uses ID IPH to distinguish between virtual accounts,
    adding iph_id may be helpful in further processing
    """
    matches = iph_id_re.search(tag_dict[tag.slug])

    if matches:  # pragma no branch
        tag_dict['iph_id'] = matches.groupdict()['iph_id']

    return tag_dict

def mBank_set_iph_id ( transactions , tag , tag_dict , * args ) : matches = iph_id_re . search ( tag_dict [ tag . slug ] ) if matches : tag_dict [ 'iph_id' ] = matches . groupdict ( ) [ 'iph_id' ] return tag_dict

mBank Collect uses ID IPH to distinguish between virtual accounts adding iph_id may be helpful in further processing


def mBank_set_tnr(transactions, tag, tag_dict, *args):
    """
    mBank Collect states TNR in transaction details as unique id for
    transactions, that may be used to identify the same transactions in
    different statement files eg. partial mt942 and full mt940
    Information about tnr uniqueness has been obtained from mBank support,
    it lacks in mt940 mBank specification.
    """

    matches = tnr_re.search(tag_dict[tag.slug])

    if matches:  # pragma no branch
        tag_dict['tnr'] = matches.groupdict()['tnr']

    return tag_dict

def mBank_set_tnr ( transactions , tag , tag_dict , * args ) : matches = tnr_re . search ( tag_dict [ tag . slug ] ) if matches : tag_dict [ 'tnr' ] = matches . groupdict ( ) [ 'tnr' ] return tag_dict

mBank Collect states TNR in transaction details as unique id for transactions that may be used to identify the same transactions in different statement files eg . partial mt942 and full mt940 Information about tnr uniqueness has been obtained from mBank support it lacks in mt940 mBank specification .


def parse(self, data):
        '''Parses mt940 data, expects a string with data

        Args:
            data (str): The MT940 data

        Returns: :py:class:`list` of :py:class:`Transaction`
        '''
        # Remove extraneous whitespace and such
        data = '\n'.join(self.strip(data.split('\n')))

        # The pattern is a bit annoying to match by regex, even with a greedy
        # match it's difficult to get both the beginning and the end so we're
        # working around it in a safer way to get everything.
        tag_re = re.compile(
            r'^:\n?(?P<full_tag>(?P<tag>[0-9]{2}|NS)(?P<sub_tag>[A-Z])?):',
            re.MULTILINE)
        matches = list(tag_re.finditer(data))

        # identify valid matches
        valid_matches = list(self.sanatize_tag_id_matches(matches))

        for i, match in enumerate(valid_matches):
            tag_id = self.normalize_tag_id(match.group('tag'))

            # get tag instance corresponding to tag id
            tag = self.tags.get(match.group('full_tag')) \
                or self.tags[tag_id]

            # Nice trick to get all the text that is part of this tag, python
            # regex matches have a `end()` and `start()` to indicate the start
            # and end index of the match.

            if valid_matches[i + 1:]:
                tag_data = \
                    data[match.end():valid_matches[i + 1].start()].strip()
            else:
                tag_data = data[match.end():].strip()

            tag_dict = tag.parse(self, tag_data)

            # Preprocess data before creating the object

            for processor in self.processors.get('pre_%s' % tag.slug, []):
                tag_dict = processor(self, tag, tag_dict)

            result = tag(self, tag_dict)

            # Postprocess the object

            for processor in self.processors.get('post_%s' % tag.slug, []):
                result = processor(self, tag, tag_dict, result)

            # Creating a new transaction for :20: and :61: tags allows the
            # tags from :20: to :61: to be captured as part of the transaction.

            if isinstance(tag, mt940.tags.Statement):
                # Transactions only get a Transaction Reference Code ID from a
                # :61: tag which is why a new transaction is created if the
                # 'id' has a value.

                if not self.transactions:
                    transaction = Transaction(self)
                    self.transactions.append(transaction)

                if transaction.data.get('id'):
                    transaction = Transaction(self, result)
                    self.transactions.append(transaction)
                else:
                    transaction.data.update(result)
            elif issubclass(tag.scope, Transaction) and self.transactions:
                # Combine multiple results together as one string, Rabobank has
                # multiple :86: tags for a single transaction

                for k, v in _compat.iteritems(result):
                    if k in transaction.data and hasattr(v, 'strip'):
                        transaction.data[k] += '\n%s' % v.strip()
                    else:
                        transaction.data[k] = v

            elif issubclass(tag.scope, Transactions):  # pragma: no branch
                self.data.update(result)

        return self.transactions

Parses mt940 data expects a string with data


def parse(src, encoding=None):
    '''
    Parses mt940 data and returns transactions object

    :param src: file handler to read, filename to read or raw data as string
    :return: Collection of transactions
    :rtype: Transactions
    '''

    def safe_is_file(filename):
        try:
            return os.path.isfile(src)
        except ValueError:  # pragma: no cover
            return False

    if hasattr(src, 'read'):  # pragma: no branch
        data = src.read()
    elif safe_is_file(src):
        with open(src, 'rb') as fh:
            data = fh.read()
    else:  # pragma: no cover
        data = src

    if hasattr(data, 'decode'):  # pragma: no branch
        exception = None
        encodings = [encoding, 'utf-8', 'cp852', 'iso8859-15', 'latin1']

        for encoding in encodings:  # pragma: no cover
            if not encoding:
                continue

            try:
                data = data.decode(encoding)
                break
            except UnicodeDecodeError as e:
                exception = e
            except UnicodeEncodeError:
                break
        else:
            raise exception  # pragma: no cover

    transactions = mt940.models.Transactions()
    transactions.parse(data)

    return transactions

def parse ( src , encoding = None ) : def safe_is_file ( filename ) : try : return os . path . isfile ( src ) except ValueError : return False if hasattr ( src , 'read' ) : data = src . read ( ) elif safe_is_file ( src ) : with open ( src , 'rb' ) as fh : data = fh . read ( ) else : data = src if hasattr ( data , 'decode' ) : exception = None encodings = [ encoding , 'utf-8' , 'cp852' , 'iso8859-15' , 'latin1' ] for encoding in encodings : if not encoding : continue try : data = data . decode ( encoding ) break except UnicodeDecodeError as e : exception = e except UnicodeEncodeError : break else : raise exception transactions = mt940 . models . Transactions ( ) transactions . parse ( data ) return transactions


def join_lines(string, strip=Strip.BOTH):
    '''
    Join strings together and strip whitespace in between if needed
    '''
    lines = []

    for line in string.splitlines():
        if strip & Strip.RIGHT:
            line = line.rstrip()

        if strip & Strip.LEFT:
            line = line.lstrip()

        lines.append(line)

    return ''.join(lines)

def join_lines ( string , strip = Strip . BOTH ) : lines = [ ] for line in string . splitlines ( ) : if strip & Strip . RIGHT : line = line . rstrip ( ) if strip & Strip . LEFT : line = line . lstrip ( ) lines . append ( line ) return '' . join ( lines )


async def json_or_text(response):
    """Turns response into a properly formatted json or text object"""
    text = await response.text()
    if response.headers['Content-Type'] == 'application/json; charset=utf-8':
        return json.loads(text)
    return text

async
def
json_or_text
(
response
)
:
text
=
await
response
.
text
(
)
if
response
.
headers
[
'Content-Type'
]
==
'application/json; charset=utf-8'
:
return
json
.
loads
(
text
)
return
text


async def limited(until):
    """Handles the message shown when we are ratelimited"""
    duration = int(round(until - time.time()))
    mins = duration / 60
    fmt = 'We have exhausted a ratelimit quota. Retrying in %.2f seconds (%.3f minutes).'
    log.warn(fmt, duration, mins)


async def request(self, method, url, **kwargs):
        """Handles requests to the API"""
        rate_limiter = RateLimiter(max_calls=59, period=60, callback=limited)
        # handles ratelimits. max_calls is set to 59 because current implementation will retry in 60s after 60 calls is reached. DBL has a 1h block so obviously this doesn't work well, as it will get a 429 when 60 is reached.

        async with rate_limiter:  # this works but doesn't 'save' over restart. need a better implementation.

            if not self.token:
                raise UnauthorizedDetected('UnauthorizedDetected (status code: 401): No TOKEN provided')

            headers = {
                'User-Agent': self.user_agent,
                'Content-Type': 'application/json'
            }

            if 'json' in kwargs:
                kwargs['data'] = to_json(kwargs.pop('json'))

            kwargs['headers'] = headers
            headers['Authorization'] = self.token

            for tries in range(5):
                async with self.session.request(method, url, **kwargs) as resp:
                    log.debug('%s %s with %s has returned %s', method,
                              url, kwargs.get('data'), resp.status)

                    data = await json_or_text(resp)

                    if 300 > resp.status >= 200:
                        return data

                    if resp.status == 429:  # we are being ratelimited
                        fmt = 'We are being rate limited. Retrying in %.2f seconds (%.3f minutes).'

                        # sleep a bit
                        retry_after = json.loads(resp.headers.get('Retry-After'))
                        mins = retry_after / 60
                        log.warning(fmt, retry_after, mins)

                        # check if it's a global rate limit (True as only 1 ratelimit atm - /api/bots)
                        is_global = True  # is_global = data.get('global', False)
                        if is_global:
                            self._global_over.clear()

                        await asyncio.sleep(retry_after, loop=self.loop)
                        log.debug('Done sleeping for the rate limit. Retrying...')

                        # release the global lock now that the
                        # global rate limit has passed
                        if is_global:
                            self._global_over.set()
                            log.debug('Global rate limit is now over.')

                        continue

                    if resp.status == 400:
                        raise HTTPException(resp, data)
                    elif resp.status == 401:
                        raise Unauthorized(resp, data)
                    elif resp.status == 403:
                        raise Forbidden(resp, data)
                    elif resp.status == 404:
                        raise NotFound(resp, data)
                    else:
                        raise HTTPException(resp, data)
            # We've run out of retries, raise.
            raise HTTPException(resp, data)


async def get_bot_info(self, bot_id):
        '''Gets the information of the given Bot ID'''
        resp = await self.request('GET', '{}/bots/{}'.format(self.BASE, bot_id))
        resp['date'] = datetime.strptime(resp['date'], '%Y-%m-%dT%H:%M:%S.%fZ')
        for k in resp:
            if resp[k] == '':
                resp[k] = None
        return resp

async def get_bot_info ( self , bot_id ) : resp = await self . request ( 'GET' , '{}/bots/{}' . format ( self . BASE , bot_id ) ) resp [ 'date' ] = datetime . strptime ( resp [ 'date' ] , '%Y-%m-%dT%H:%M:%S.%fZ' ) for k in resp : if resp [ k ] == '' : resp [ k ] = None return resp


async def get_bots(self, limit, offset):
        '''Gets an object of bots on DBL'''
        if limit > 500:
            limit = 50
        return await self.request('GET', '{}/bots?limit={}&offset={}'.format(self.BASE, limit, offset))

async def get_bots ( self , limit , offset ) : if limit > 500 : limit = 50 return await self . request ( 'GET' , '{}/bots?limit={}&offset={}' . format ( self . BASE , limit , offset ) )


def read(self):
        """Read incoming message."""
        packet = self.packet
        with self.__read_lock:
            buffer = self.__buffer
            while len(buffer) < packet:
                buffer += self._read_data()
            length = self.__unpack(buffer[:packet])[0] + packet
            while len(buffer) < length:
                buffer += self._read_data()
            term, self.__buffer = decode(buffer[packet:])
        return term

def read ( self ) : packet = self . packet with self . __read_lock : buffer = self . __buffer while len ( buffer ) < packet : buffer += self . _read_data ( ) length = self . __unpack ( buffer [ : packet ] ) [ 0 ] + packet while len ( buffer ) < length : buffer += self . _read_data ( ) term , self . __buffer = decode ( buffer [ packet : ] ) return term


def write(self, message):
        """Write outgoing message."""
        data = encode(message, compressed=self.compressed)
        length = len(data)
        data = self.__pack(length) + data
        with self.__write_lock:
            while data:
                try:
                    n = os.write(self.out_d, data)
                except OSError as why:
                    if why.errno in (errno.EPIPE, errno.EINVAL):
                        raise EOFError()
                    raise
                if not n:
                    raise EOFError()
                data = data[n:]
        return length + self.packet

def write ( self , message ) : data = encode ( message , compressed = self . compressed ) length = len ( data ) data = self . __pack ( length ) + data with self . __write_lock : while data : try : n = os . write ( self . out_d , data ) except OSError as why : if why . errno in ( errno . EPIPE , errno . EINVAL ) : raise EOFError ( ) raise if not n : raise EOFError ( ) data = data [ n : ] return length + self . packet


def close(self):
        """Close port."""
        os.close(self.in_d)
        os.close(self.out_d)

def close ( self ) : os . close ( self . in_d ) os . close ( self . out_d )