Welcome to xoutil’s documentation!

xoutil is a collection of disparate utilities that does not conform a framework for anything. xoutil is essentially an extension to the Python’s standard library.

What’s new in 1.7.12

  • xoutil.datetime.EmptyTimeSpan is now pickable.

Contents:

xoutil – Collection of tools. Top-level imports.

xoutil.Unset = Unset

Instances are custom logical values (True or False).

See _get_instance() method for information on constructor arguments.

For example:

>>> true = Logical('true', True)
>>> false = Logical('false')
>>> none = Logical('false')
>>> unset = Logical('unset')

>>> class X(object):
...      attr = None

>>> getattr(X(), 'attr') is not None
False

>>> getattr(X(), 'attr', false) is not false
True

>>> none is false
True

>>> false == False
True

>>> false == unset
True

>>> false is unset
False

>>> true == True
True
xoutil.Ignored = Ignored

Instances are custom logical values (True or False).

See _get_instance() method for information on constructor arguments.

For example:

>>> true = Logical('true', True)
>>> false = Logical('false')
>>> none = Logical('false')
>>> unset = Logical('unset')

>>> class X(object):
...      attr = None

>>> getattr(X(), 'attr') is not None
False

>>> getattr(X(), 'attr', false) is not false
True

>>> none is false
True

>>> false == False
True

>>> false == unset
True

>>> false is unset
False

>>> true == True
True

xoutil.annotate - Py3k compatible annotations for Python 2

Provides Python 3k forward-compatible (PEP 3107) annotations.

Note

The signature argument for the annotate() in this module may not work on other python implementations than CPython. Currently, Pypy passes all but local variable tests.

xoutil.annotate.annotate(signature=None, **annotations)[source]

Annotates a function with a Python 3k forward-compatible __annotations__ mapping.

See PEP 3107 for more details about annotations.

Parameters:
  • signature
    A string with the annotated signature of the
    decorated function.

    This string should follow the annotations syntax in PEP 3107. But there are several deviations from the PEP text:

    • There’s no support for the full syntax of Python 2 expressions; in particular nested arguments are not supported since they are deprecated and are not valid in Py3k.
    • Specifying defaults is no supported (nor needed). Defaults are placed in the signature of the function.
    • In the string it makes no sense to put an argument without an annotation, so this will raise an exception (SyntaxError).
  • keyword_annotations

    These are each mapped to a single annotation.

    Since you can’t include the ‘return’ keyword argument for the annotation related with the return of the function, we provide several alternatives: if any of the following keywords arguments is provided (tested in the given order): ‘return_annotation’, ‘_return’, ‘__return’; then it will be considered the ‘return’ annotation, the rest will be regarded as other annotations.

In any of the previous cases, you may provide more (or less) annotations than possible by following the PEP syntax. This is not considered an error, since the PEP allows annotations to be modified by others means.

If you provide a signature string and keywords annotations, the keywords will take precedence over the signature:

>>> @annotate('() -> list', return_annotation=tuple)
... def otherfunction():
...    pass

>>> otherfunction.__annotations__.get('return') is tuple
True

When parsing the signature the locals and globals in the context of the declaration are taken into account:

>>> interface = object # let's mock of ourselves
>>> class ISomething(interface):
...    pass

>>> @annotate('(a: ISomething) -> ISomething')
... def somewhat(a):
...     return a

>>> somewhat.__annotations__.get('a')     
<class '...ISomething'>

xoutil.bases - Numeric base 32 and base 64 integer representations

xoutil.bases.int2str(number, base=62)[source]

Return the string representation of an integer using a base.

Parameters:base (Either an integer or a string with a custom table.) – The base.

Examples:

>>> int2str(65535, 16)
'ffff'

>>> int2str(65535)
'h31'

>>> int2str(65110208921, 'merchise')
'ehimseiemsce'

>>> int2str(651102, 2)
'10011110111101011110'
xoutil.bases.str2int(src, base=62)[source]

Return the integer decoded from a string representation using a base.

Parameters:base (Either an integer or a string with a custom table.) – The base.

Examples:

>>> str2int('ffff', 16)
65535

>>> str2int('1c', 16) == int('1c', 16)
True

>>> base = 'merchise'
>>> number = 65110208921
>>> str2int(int2str(number, base), base) == number
False

>>> base = 32
>>> str2int(int2str(number, base), base) == number
True
class xoutil.bases.B32[source]

Handles base-32 conversions.

In base 32, each 5-bits chunks are represented by a single “digit”. Digits comprises all symbols in 0..9 and a..v.

>>> B32.inttobase(32) == '10'
True
>>> B32.basetoint('10')
32
class xoutil.bases.B64[source]

Handles [a kind of] base 64 conversions.

This is not standard base64, but a reference-friendly base 64 to help the use case of generating a short reference.

In base 64, each 6-bits chunks are represented by a single “digit”. Digits comprises all symbols in 0..9, a..z, A..Z and the three symbols: ()[.

>>> B64.inttobase(64) == '10'
True
>>> B64.basetoint('10')
64

Warning

In this base, letters are case sensitive:

>>> B64.basetoint('a')
10

>>> B64.basetoint('A')
36

xoutil.bound – Helpers for bounded execution of co-routines.

New in version 1.6.3: Pre-release version.

Warning

This module is still in heavy development. The API is flux. It will be declared stable only after release 1.8.0. Even the module name might change in the future.

A bounded execution model

Some features are easy to implement using a generator or co-routine (PEP 342). For instance, you might want to “report units of work” one at a time. These kind of features could be easily programmed without any bounds whatsoever, and then you might “weave” the bounds.

This module helps to separate the work-doing function from the boundary-tests definitions.

This document uses the following terminology:

unbounded function

This is the function that does the actual work without testing for any boundary condition. Boundary conditions are not “natural causes” of termination for the algorithm but conditions imposed elsewhere: the environment, resource management, etc.

This function must return a generator, called the unbounded generator.

unbounded generator
The generator returned by an unbounded function. This generator is allowed to yield forever, although it could terminate by itself. So this is actually a possibly unbounded generator, but we keep the term to emphasize.
boundary condition

It’s a condition that does not belong to the logical description of any algorithm. When this condition is met it indicates that the unbounded generator should be closed. The boundary condition is tested each time the unbounded generator yields.

A boundary condition is usually implemented in a single function called the boundary definition.

boundary definition

A function that implements a boundary condition. This function must comply with the boundary protocol (see boundary()).

Sometimes we identify the boundary condition with its boundary definition.

bounded function
It’s the result of applying a boundary definition to an unbounded function.
bounded generator
It’s the result of applying a boundary condition to an unbounded generator.

The bounded execution model takes at least an unbounded generator and a boundary condition. Applying the boundary condition to the unbounded generator ultimately results in a bounded generator, which will behave almost equivalently to the unbounded generator but will stop when the boundary condition yields True or when the unbounded generator itself is exhausted.

Included boundary conditions

xoutil.bound.timed(maxtime)[source]

Becomes True after a given amount of time.

The bounded generator will be allowed to yields values until the maxtime time frame has elapsed.

Usage:

@timed(timedelta(seconds=60))
def do_something_in_about_60s():
    while True:
        yield

Note

This is a very soft limit.

We can’t actually guarrant any enforcement of the time limit. If the bounded generator takes too much time or never yields this predicated can’t do much. This usually helps with batch processing that must not exceed (by too much) a given amount of time.

The timer starts just after the next() function has been called for the predicate initialization. So if the maxtime given is too short this predicated might halt the execution of the bounded function without allowing any processing at all.

If maxtime is not a timedelta, the timedelta will be computed as timedelta(seconds=maxtime).

xoutil.bound.times(n)[source]

Becomes True after a given after the nth item have been produced.

xoutil.bound.accumulated(mass, *attrs, initial=0)[source]

Becomes True after accumulating a given “mass”.

mass is the maximum allowed to accumulate. This is usually a positive number. Each value produced by the unbounded generator is added together. Yield True when this amount to more than the given mass.

If any attrs are provided, they will be considered attributes (or keys) to search inside the yielded data from the bounded function. If no attrs are provided the whole data is accumulated, so it must allow addition. The attribute to be summed is extracted with get_first_of(), so only the first attribute found is added.

If the keyword argument initial is provided the accumulator is initialized with that value. By default this is 0.

xoutil.bound.pred(func, skipargs=True)[source]

Allow “normal” functions to engage within the boundary protocol.

func should take a single argument and return True if the boundary condition has been met.

If skipargs is True then function func will not be called with the tuple (args, kwargs) upon initialization of the boundary, in that case only yielded values from the unbounded generator are passed. If you need to get the original arguments, set skipargs to False, in this case the first time func is called will be passed a single argument (arg, kwargs).

Example:

>>> @pred(lambda x: x > 10)
... def fibonacci():
...     a, b = 1, 1
...     while True:
...        yield a
...        a, b = b, a + b

>>> fibonacci()
13
xoutil.bound.until_errors(*errors)[source]

Becomes True after any of errors has been raised.

Any other exceptions (except GeneratorExit) is propagated. You must pass at least an error.

Normally this will allow some possibly long jobs to be interrupted (SoftTimeLimitException in celery task, for instance) but leave some time for the caller to clean up things.

It’s assumed that your job can be properly finalized after any of the given exceptions has been raised.

Parameters:on_error – A callable that will only be called if the boundary condition is ever met, i.e if any of errors was raised. The callback is called before yielding True.

New in version 1.7.2.

Changed in version 1.7.5: Added the keyword argument on_error.

xoutil.bound.until(time=None, times=None, errors=None)[source]

An idiomatic alias to other boundary definitions.

  • until(maxtime=n) is the same as timed(n).
  • until(times=n) is the same as times(n).
  • until(pred=func, skipargs=skip) is the same as pred(func, skipargs=skip).
  • until(errors=errors, **kwargs) is the same as until_errors(*errors, **kwargs).
  • until(accumulate=mass, path=path, initial=initial) is the same as
    accumulated(mass, *path.split('.'), initial=initial)

Warning

You cannot mix many calls.

New in version 1.7.2.

Chaining several boundary conditions

To created a more complex boundary than the one provided by a single condition you could use the following high-level boundaries:

xoutil.bound.whenany(*boundaries)[source]

An OR-like boundary condition.

It takes several boundaries and returns a single one that behaves like the logical OR, i.e, will yield True when any of its subordinate boundary conditions yield True.

Calls close() of all subordinates upon termination.

Each boundary should be either:

  • A “bare” boundary definition that takes no arguments.
  • A boundary condition (i.e an instance of BoundaryCondition). This is result of calling a boundary definition.
  • A generator object that complies with the boundary protocol. This cannot be tested upfront, a misbehaving generator will cause a RuntimeError if a boundary protocol rule is not followed.

Any other type is a TypeError.

xoutil.bound.whenall(*boundaries)[source]

An AND-like boundary condition.

It takes several boundaries and returns a single one that behaves like the logical AND i.e, will yield True when all of its subordinate boundary conditions have yielded True.

It ensures that once a subordinate yields True it won’t be sent more data, no matter if other subordinates keep on running and consuming data.

Calls close() of all subordinates upon termination.

Each boundary should be either:

  • A “bare” boundary definition that takes no arguments.
  • A boundary condition (i.e an instance of BoundaryCondition). This is result of calling a boundary definition.
  • A generator object that complies with the boundary protocol. This cannot be tested upfront, a misbehaving generator will cause a RuntimeError if a boundary protocol rule is not followed.

Any other type is a TypeError.

Defining boundaries

If none of the boundaries defined deals with a boundary condition you have, you may create another one using boundary(). This is usually employed as decorator on the boundary definition.

xoutil.bound.boundary(definition)[source]

Helper to define a boundary condition.

The definition must be a function that returns a generator. The following rules must be followed. Collectively these rules are called the boundary protocol.

  • The boundary definition will yield True when and only when the boundary condition is met. Only the value True will signal the boundary condition.

  • The boundary definition must yield at least 2 times:

    • First it will be called its next() method to allow for initialization of internal state.
    • Immediately after, it will be called its send() passing the tuple (args, kwargs) with the arguments passed to the unbounded function. At this point the boundary definition may yield True to halt the execution. In this case, the unbounded generator won’t be asked for any value.
  • The boundary definition must yield True before terminating with a StopIteration. For instance the following definition is invalid cause it ends without yielding True:

    @boundary
    def invalid():
        yield
        yield False
    
  • The boundary definition must deal with GeneratorExit exceptions properly since we call the close() method of the generator upon termination. Termination occurs when the unbounded generator stops by any means, even when the boundary condition yielded True or the generator itself is exhausted or there’s an error in the generator.

    Both whenall() and whenany() call the close() method of all their subordinate boundary conditions.

    Most of the time this reduces to not catching GeneratorExit exceptions.

A RuntimeError may happen if any of these rules is not followed by the definition. Furthermore, this error will occur when invoking the bounded function and not when applying the boundary to the unbounded generator.

Illustration of a boundary

Let’s explain in detail the implementation of times() as an example of how a boundary condition could be implemented.

1
2
3
4
5
6
7
8
9
@boundary
def times(n):
    '''Becomes True after the `nth` item have been produced.'''
    passed = 0
    yield False
    while passed < n:
        yield False
        passed += 1
    yield True

We implemented the boundary condition via the boundary() helper. This helpers allows to implement the boundary condition via a boundary definition (the function above). The boundary helper takes the definition and builds a BoundaryCondition instance. This instance can then be used to decorate the unbounded function, returning a bounded function (a Bounded instance).

When the bounded function is called, what actually happens is that:

  • First the boundary condition is invoked passing the n argument, and thus we obtain the generator from the times function.

  • We also get the generator from the unbounded function.

  • Then we call next(boundary) to allow the times boundary to initialize itself. This runs the code of the times definition up to the line 5 (the first yield statement).

  • The bounded function ignores the message from the boundary at this point.

  • Then it sends the arguments passed to original function via the send() method of the boundary condition generator.

  • This unfreezes the boundary condition that now tests whether passes is less that n. If this is true, the boundary yields False and suspends there at line 7.

  • The bounded function see that message is not True and asks the unbounded generator for its next value.

  • Then it sends that value to the boundary condition generator, which resumes execution at line 8. The value sent is ignored and passes gets incremented by 1.

  • Again the generator asks if passes is less that n. If passes has reached n, it will execute line 9, yielding True.

  • The bounded function see that the boundary condition is True and calls the close() method to the boundary condition generator.

  • This is like raising a GeneratorExit just after resuming the times below line 9. The error is not trapped and propagates the close() method of the generator knows this means the generator has properly finished.

    Note

    Other boundaries might need to deal with GeneratorExit explicitly.

  • Then the bounded function regains control and calls the close() method of the unbounded generator, this effectively raises a GeneratorExit inside the unbounded generator, which if untreated means everything went well.

If you look at the implementation of the included boundary conditions, you’ll see that all have the same pattern:

  1. Initialization code, followed by a yield False statement. This is a clear indicator that the included boundary conditions disregard the first message (the arguments to the unbounded function).
  2. A looping structure that tests the condition has not been met and yields False at each cycle.
  3. The yield True statement outside the loop to indicate the boundary condition has been met.

This pattern is not an accident. Exceptionally whenall() and whenany() lack the first standalone yield False because they must not assume all its subordinate predicates will ignore the first message.

Internal API

class xoutil.bound.Bounded(target)[source]

The bounded function.

This is the result of applying a boundary definition to an unbounded function (or generator).

If target is a function this instance can be called several times. If it’s a generator then it will be closed after either calling (__call__) this instance, or consuming the generator given by generate().

This class is actually subclassed inside the apply() so that the weaving boundary definition with the target unbounded function is not exposed.

__call__(*args, **kwargs)[source]

Return the last value from the underlying bounded generator.

generate(*args, **kwargs)[source]

Return the bounded generator.

This method exposes the bounded generator. This allows you to “see” all the values yielded by the unbounded generator up to the point when the boundary condition is met.

class xoutil.bound.BoundaryCondition(definition, name=None, errors=None)[source]

Embodies the boundary protocol.

The definition argument must a function that implements a boundary definition. This function may take arguments to initialize the state of the boundary condition.

Instances are callables that will return a Bounded subclass specialized with the application of the boundary condition to a given unbounded function (target). For instance, times(6) returns a class, that when instantiated with a target represents the bounded function that takes the 6th valued yielded by target.

If the definition takes no arguments for initialization you may pass the target directly. This is means that if __call__() receives arguments they will be used to instantiate the Bounded subclass, ie. this case allows only a single argument target.

If errors is not None it should be a tuple of exceptions to catch and throw inside the boundary condition definition. Other exceptions, beside GeneratorExit and StopIteration, are not handled (so the bubble up). See until_error().

An example: time bounded batch processing

We have a project in which we need to send emails inside a cron task (celery is not available). Emails to be sent are placed inside an Outbox but we may only spent about 60 seconds to send as many emails as we can. If our emails are reasonably small (i.e will be delivered to the SMTP server in a few miliseconds) we could use the timed() predicate to bound the execution of the task:

@timed(50)
def send_emails():
   outbox = Outbox.open()
   try:
      for message in outbox:
         emailbackend.send(message)
         outbox.remove(message)
         yield message
   except GeneratorExit:
      # This means the time we were given is off.
      pass
   finally:
      outbox.close()  # commit the changes to the outbox

Notice that you must enclose your batch-processing code in a try statement if you need to somehow commit changes. Since we may call the close() method of the generator to signal that it must stop.

A finally clause is not always appropriated cause an error that is not GeneratorExit error should not commit the data unless you’re sure data changes that were made before the error could be produced. In the code above the only place in the code above where an error could happen is the sending of the email, and the data is only touched for each email that is actually sent. So we can safely close our outbox and commit the removal of previous message from the outbox.

Using the Bounded.generate() method

Calling a bounded generator simply returns the last valued produced by the unbounded generator, but sometimes you need to actually see all the values produced. This is useful if you need to meld several generators with partially overlapping boundary conditions.

Let’s give an example by extending a bit the example given in the previous section. Assume you now need to extend your cron task to also read an Inbox as much as it can and then send as many messages as it can. Both things should be done under a given amount of time, however the accumulated size of sent messages should not surpass a threshold of bytes to avoid congestion.

For this task you may use both timed() and accumulated(). But you must apply accumulated() only to the process of sending the messages and the timed boundary to the overall process.

This can be accomplished like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
def communicate(interval, bandwidth):
    from itertools import chain as meld

    def receive():
        for message in Inbox.receive():
           yield message

    @accumulated(bandwith, 'size')
    def send():
        for message in Outbox.messages():
            yield message

    @timed(interval)
    def execute():
        for _ in meld(receive(), send.generate()):
            yield
    return execute()

Let’s break this into its parts:

  • The receive function reads the Inbox and yields each message received.

    It is actually an unbounded function but don’t want to bound its execution in isolation.

  • The send unbounded function sends every message we have in the Outbox and yields each one. In this case we can apply the accumulated boundary to get a Bounded instance.

  • Then we define an execute function bounded by timed. This function melds the receive and send processes, but we can’t actually call send because we need to yield after each message has been received or sent. That’s why we need to call the generate() so that the time boundary is also applied to the sending process.

Note

The structure from this example is actually taken from a real program, although simplified to serve better for learning. For instance, in our real-world program bandwidth could be None to indicate no size limit should be applied to the sending process. Also in the example we’re not actually saving nor sending messages!

xoutil.cli – Command line application facilities

Define tools for command-line interface (CLI) applications.

CLi is a means of interaction with a computer program where the user (or client) issues commands to the program in the form of successive lines of text (command lines).

New in version 1.4.1.

class xoutil.cli.Command[source]

A command base registry.

There are several methods to register new commands:

  • Inheriting from this class
  • Using the ABC mechanism of register virtual subclasses.
  • Registering a class with the method “__commands__” defined.

If the method “__commands__” is used, it must be a class or static method.

Command names are calculated as class names in lower case inserting a hyphen before each new capital letter. For example “MyCommand” will be used as “my-command”.

Each command could include its own argument parser, but it isn’t used automatically, all arguments will be passed as a single parameter to run() removing the command when obtained from “sys.argv”.

run(args=None)[source]

Must return a valid value for “sys.exit”

classmethod set_default_command(cmd=None)[source]

A default command can be defined for call it when no one is specified.

A command is detected when its name appears as the first command-line argument.

To specify a default command, use this method with the command as a string (the command name) or the command class.

If the command is specified, then the calling class is the selected one.

For example:

>>> Command.set_default_command('server')  
>>> Server.set_default_command()           
>>> Command.set_default_command(Server)    
class xoutil.cli.Help[source]

Show all commands

Define the class attribute __order__ to sort commands in special command “help”.

Commands could define its help in the first line of a sequence of documentations until found:

  • command class,
  • “run” method,
  • definition module.

This command could not be overwritten unless using the class attribute:

__override__ = True
classmethod get_arg_parser()[source]

This is an example on how to build local argument parser.

Use class method “get

Applications

A simple main() entry point for CLI based applications.

This module provides an example of how to use xoutil.cli to create a CLI application.

xoutil.cli.app.main(default=None)[source]

Execute a command, it can be given as the first program argument or it’s the default command is defined.

Tools

Utilities for command-line interface (CLI) applications.

xoutil.cli.tools.command_name(cls)[source]

Command names are calculated as class names in lower case inserting a hyphen before each new capital letter. For example “MyCommand” will be used as “my-command”.

It’s defined as an external function because a class method don’t apply to minimal commands (those with only the “run” method).

Example:

>>> class SomeCommand(object):
...     pass

>>> command_name(SomeCommand) == 'some-command'
True

If the command class has an attribute command_cli_name, this will be used instead:

>>> class SomeCommand(object):
...    command_cli_name = 'adduser'

>>> command_name(SomeCommand) == 'adduser'
True

It’s an error to have a non-string command_cli_name attribute:

>>> class SomeCommand(object):
...    command_cli_name = None

>>> command_name(SomeCommand)  
Traceback (most recent call last):
   ...
TypeError: Attribute 'command_cli_name' must be a string.
xoutil.cli.tools.program_name()[source]

Calculate the program name from “sys.argv[0]”.

xoutil.collections - High-performance container datatypes

This module extends the standard library’s collections. You may use it as a drop-in replacement in many cases.

Avoid importing * from this module since this is different in Python 2.7 and Python 3.3. Notably importing abc is not available in Python 2.7.

We have backported several Python 3.3 features but not all.

class xoutil.collections.defaultdict[source]

A hack for collections.defaultdict that passes the key and a copy of self as a plain dict (to avoid infinity recursion) to the callable.

Examples:

>>> from xoutil.collections import defaultdict
>>> d = defaultdict(lambda key, d: 'a')
>>> d['abc']
'a'

Since the second parameter is actually a dict-copy, you may (naively) do the following:

>>> d = defaultdict(lambda k, d: d[k])
>>> d['abc']
Traceback (most recent call last):
    ...
KeyError: 'abc'

You may use this class as a drop-in replacement for collections.defaultdict:

>>> d = defaultdict(lambda: 1)
>>> d['abc']
1
class xoutil.collections.opendict[source]

A dictionary implementation that mirrors its keys as attributes:

>>> d = opendict({'es': 'spanish'})
>>> d.es
'spanish'

>>> d['es'] = 'espanol'
>>> d.es
'espanol'

Setting attributes does not makes them keys.

class xoutil.collections.Counter(iterable=None, **kwds)[source]

Dict subclass for counting hashable items. Sometimes called a bag or multiset. Elements are stored as dictionary keys and their counts are stored as dictionary values.

>>> c = Counter('abcdeabcdabcaba')  # count elements from a string
>>> c.most_common(3)                # three most common elements
[('a', 5), ('b', 4), ('c', 3)]
>>> sorted(c)                       # list all unique elements
['a', 'b', 'c', 'd', 'e']
>>> ''.join(sorted(c.elements()))   # list elements with repetitions
'aaaaabbbbcccdde'
>>> sum(c.values())            # total of all counts
15
>>> c['a']                     # count of letter 'a'
5
>>> for elem in 'shazam':      # update counts from an iterable
...     c[elem] += 1           # by adding 1 to each element's count
>>> c['a']                     # now there are seven 'a'
7
>>> del c['b']                 # remove all 'b'
>>> c['b']                     # now there are zero 'b'
0
>>> d = Counter('simsalabim')  # make another counter
>>> c.update(d)                # add in the second counter
>>> c['a']                     # now there are nine 'a'
9
>>> c.clear()                  # empty the counter
>>> c
Counter()

Note: If a count is set to zero or reduced to zero, it will remain in the counter until the entry is deleted or the counter is cleared:

>>> c = Counter('aaabbc')
>>> c['b'] -= 2               # reduce the count of 'b' by two
>>> c.most_common()           # 'b' is still in, but its count is zero
[('a', 3), ('c', 1), ('b', 0)]

Note

Backported from Python 3.3. In Python 3.3 this is an alias.

class xoutil.collections.OrderedDict(*args, **kwds)[source]

Dictionary that remembers insertion order

Note

Backported from Python 3.3. In Python 3.3 this is an alias.

class xoutil.collections.OpenDictMixin[source]

A mixin for mappings implementation that expose keys as attributes:

>>> from xoutil.objects import SafeDataItem as safe

>>> class MyOpenDict(OpenDictMixin, dict):
...     __slots__ = safe.slot(OpenDictMixin.__cache_name__, dict)

>>> d = MyOpenDict({'es': 'spanish'})
>>> d.es
'spanish'

>>> d['es'] = 'espanol'
>>> d.es
'espanol'

When setting or deleting an attribute, the attribute name is regarded as key in the mapping if neither of the following condition holds:

  • The name is a slot.
  • The object has a __dict__ attribute and the name is key there.

This mixin defines the following features that can be redefined:

_key2identifier

Protected method, receive a key as argument and return a valid identifier that is used instead the key as an extended attribute.

__cache_name__

Inner field to store a cached mapping between actual keys and calculated attribute names. The field must be always implemented as a SafeDataItem descriptor and must be of type dict. There are two ways of implementing this:

  • As a slot. The first time of this implementation is an example. Don’t forget to pass the second parameter with the constructor dict.

  • As a normal descriptor:

    >>> from xoutil.objects import SafeDataItem as safe
    >>> class MyOpenDict(OpenDictMixin, dict):
    ...     safe(OpenDictMixin.__cache_name__, dict)
    
Classes or Mixins that can be integrated with dict by inheritance
must not have a __slots__ definition. Because of that, this mixin must not declare any slot. If needed, it must be declared explicitly in customized classed like in the example in the first part of this documentation or in the definition of opendict class.
class xoutil.collections.OrderedSmartDict(*args, **kwds)[source]

A combination of the OrderedDict with the SmartDictMixin.

Warning

Initializing with kwargs does not ensure any initial ordering, since Python’s keyword dict is not ordered. Use a list/tuple of pairs instead.

class xoutil.collections.SmartDictMixin[source]

A mixin that extends the update method of dictionaries

Standard method allow only one positional argument, this allow several.

Note on using mixins in Python: method resolution order is calculated in the order of inheritance, if a mixin is defined to overwrite behavior already existent, use first that classes with it. See SmartDict below.

class xoutil.collections.StackedDict(*args, **kwargs)[source]

A multi-level mapping.

A level is entered by using the push() and is leaved by calling pop().

The property level returns the actual number of levels.

When accessing keys they are searched from the latest level “upwards”, if such a key does not exists in any level a KeyError is raised.

Deleting a key only works in the current level; if it’s not defined there a KeyError is raised. This means that you can’t delete keys from the upper levels without popping.

Setting the value for key, sets it in the current level.

Changed in version 1.5.2: Based on the newly introduced ChainMap.

pop()[source]

A deprecated alias for pop_level().

Deprecated since version 1.7.0.

push(*args, **kwargs)[source]

A deprecated alias for push_level().

Deprecated since version 1.7.0.

level

Return the current level number.

The first level is 0. Calling push() increases the current level (and returns it), while calling pop() decreases the current level (if possible).

peek()[source]

Peeks the top level of the stack.

Returns a copy of the top-most level without any of the keys from lower levels.

Example:

>>> sdict = StackedDict(a=1, b=2)
>>> sdict.push(c=3)  # it returns the level...
1
>>> sdict.peek()
{'c': 3}
pop_level()[source]

Pops the last pushed level and returns the whole level.

If there are no levels in the stacked dict, a TypeError is raised.

Returns:A dict containing the poped level.
push_level(*args, **kwargs)[source]

Pushes a whole new level to the stacked dict.

Parameters:
  • args – Several mappings from which the new level will be initialled filled.
  • kwargs – Values to fill the new level.
Returns:

The pushed level number.

class xoutil.collections.ChainMap(*maps)[source]

A ChainMap groups multiple dicts or other mappings together to create a single, updateable view. If no maps are specified, a single empty dictionary is provided so that a new chain always has at least one mapping.

The underlying mappings are stored in a list. That list is public and can accessed or updated using the maps attribute. There is no other state.

Lookups search the underlying mappings successively until a key is found. In contrast, writes, updates, and deletions only operate on the first mapping.

A ChainMap incorporates the underlying mappings by reference. So, if one of the underlying mappings gets updated, those changes will be reflected in ChainMap.

All of the usual dictionary methods are supported. In addition, there is a maps attribute, a method for creating new subcontexts, and a property for accessing all but the first mapping:

maps

A user updateable list of mappings. The list is ordered from first-searched to last-searched. It is the only stored state and can be modified to change which mappings are searched. The list should always contain at least one mapping.

new_child(m=None)[source]

Returns a new ChainMap containing a new map followed by all of the maps in the current instance. If m is specified, it becomes the new map at the front of the list of mappings; if not specified, an empty dict is used, so that a call to d.new_child() is equivalent to: ChainMap({}, *d.maps). This method is used for creating subcontexts that can be updated without altering values in any of the parent mappings.

Changed in version 1.5.5: The optional m parameter was added.

parents

Property returning a new ChainMap containing all of the maps in the current instance except the first one. This is useful for skipping the first map in the search. Use cases are similar to those for the nonlocal keyword used in nested scopes. A reference to d.parents is equivalent to: ChainMap(*d.maps[1:]).

Note

Backported from Python 3.4. In Python 3.4 this is an alias.

class xoutil.collections.PascalSet(*others)[source]

Collection of unique integer elements (implemented with intervals).

PascalSet(*others) -> new set object

New in version 1.7.0.

class xoutil.collections.BitPascalSet(*others)[source]

Collection of unique integer elements (implemented with bit-wise sets).

BitPascalSet(*others) -> new bit-set object

New in version 1.7.0..

xoutil.connote – Predicates

A predicate is seen as a property that a subject has or is characterized by. A predicate is therefore an expression that can be true of something (involve as a necessary condition of consequence). Thus, the expression “is moving” is true of those things that are moving.

The main class of this module is Predicate.

New in version 1.7.0.

class xoutil.connote.Predicate(*args, **kwargs)[source]

A definition of a validation function using a grammar of simple predicates.

All parameters are checkers; those given by name (keyword arguments) are used to produce named anonymous checkers, for example:

>>> from xoutil.predicate import Predicate
>>> p = Predicate(int, is_valid_age=lambda age: 0 < age <= 120)

A lambda wrapper (lw()) can be used to decorate anonymous functions; so, last declaration is equivalent to:

>>> p = Predicate(int, lw('is_valid_age', lambda age: 0 < age <= 120))

There is a special keyword argument (‘__named__’); if present and True; a name will be generated for the predicate.

Each checker could be:

  • A type (or tuple of types) to test with isinstance(value, checker)
  • A set, a value will be valid if is contained in the set.
  • A mapping, a value will be valid if is contained in the mapping and its value is True.
  • A tuple of other inner checkers, if any of the checkers validates a value, the value is valid (OR).
  • A list of other inner checkers, all checkers must validate the value (AND).
  • A callable that receives the value and returns True if the value is valid.
  • True, False or any instance of Logical could be used as checkers always validating or invalidating the value.
  • An empty list is synonym of True, an empty tuple, set or mapping is synonym of False.

Any other value will return False (fail).

Note

Above definition is controversial, maybe a exception must be raised if a invalid checker is used.

With this class, it could be built real type checkers, for example:

>>> from xoutil.predicate import Predicate as pp
>>> numbers = (int, float)
>>> is_valid_age = pp(numbers, valid_age=lambda age: 0 < age <= 120)
>>> is_valid_age(100)
True

>>> is_valid_age(130)
False

>>> is_valid_age(85.5)
True

Other simple example:

>>> always_true = predicate(True)
>>> always_true(False)
True

>>> always_false = predicate(False)
>>> always_false(True)
False

>>> always_true = predicate()
>>> always_true(1)
True

>>> always_true('any string')
True

>>> always_false = predicate(())
>>> always_false(1)
False

>>> always_false('any string')
False
get_name()[source]

Get a name representation suitable for this predicate.

This method is used in the constructor to cache this value if keyword argument ‘__named__’ is given and True.

xoutil.context - Simple execution contexts

A context manager for execution context flags.

xoutil.context.context

alias of Context

class xoutil.context.Context(*args, **kwargs)[source]

An execution context manager with parameters (or flags).

Use as:

>>> SOME_CONTEXT = object()
>>> from xoutil.context import context
>>> with context(SOME_CONTEXT):
...     if context[SOME_CONTEXT]:
...         print('In context SOME_CONTEXT')
In context SOME_CONTEXT

Note the difference creating the context and checking it: for entering a context you should use ` context(name)`` for testing whether some piece of code is being executed inside a context you should use context[name]; you may also use the syntax name in context.

When an existing context is re-enter, the former one is reused. Nevertheless, the data stored in each context is local to each level.

For example:

>>> with context('A', b=1) as a1:
...   with context('A', b=2) as a2:
...       print(a1 is a2)
...       print(a2['b'])
...   print(a1['b'])
True
2
1

For data access, a mapping interface is provided for all contexts. If a data slot is deleted at some level, upper level is used to read values. Each new written value is stored in current level without affecting upper levels.

For example:

>>> with context('A', b=1) as a1:
...   with context('A', b=2) as a2:
...       del a2['b']
...       print(a2['b'])
1

It is an error to reuse a context directly like in:

>>> with context('A', b=1) as a1:   
...   with a1:
...       pass
Traceback (most recent call last):
...
RuntimeError: Entering the same context level twice! ...

Note

About thread-locals and contexts.

The context uses internally a thread-local instance to keep context stacks in different threads from seeing each other.

If, when this module is imported, greenlet is imported already, greenlet isolation is also warranted (which implies thread isolation).

If you use collaborative multi-tasking based in other framework other than greenlet, you must ensure to monkey patch the threading.local class so that isolation is kept.

In future releases of xoutil, we plan to provide a way to inject a “process” identity manager so that other frameworks be easily integrated.

Changed in version 1.7.1: Changed the test about greenlet. Instead of testing for greenlet to be importable, test it is imported already.

Changed in version 1.6.9: Added direct greenlet isolation and removed the need for gevent.local.

New in version 1.6.8: Uses gevent.local if available to isolate greenlets.

xoutil.crypto - Other cryptographic services

General security tools.

Adds the ability to generate new passwords using a source pass-phrase and a secury strong level.

xoutil.crypto.generate_password(pass_phrase, level=3)[source]

Generate a password from a source pass-phrase and a security level.

Parameters:
  • pass_phrase – String pass-phrase to be used as base of password generation process.
  • level – Numerical security level (the bigger the more secure, but don’t exaggerate!).

When pass_phrase is a valid string, level means a generation method. Each level implies all other with an inferior numerical value.

There are several definitions with numerical values for level (0-4):

PASS_PHRASE_LEVEL_BASIC

Generate the same pass-phrase, just removing invalid characters and converting the result to lower-case.

PASS_PHRASE_LEVEL_MAPPED

Replace some characters with new values: 'e'->'3', 'i'->'1', 'o'->'0', 's'->'5'.

PASS_PHRASE_LEVEL_MAPPED_MIXED

Consonants characters before ‘M’ (included) are converted to upper-case, all other are kept lower-case.

PASS_PHRASE_LEVEL_MAPPED_DATED

Adds a suffix with the year of current date (“<YYYY>”).

PASS_PHRASE_LEVEL_STRICT

Randomly scramble previous result until unbreakable strong password is obtained.

If pass_phrase is None or an empty string, generate a “secure salt” (a password not based in a source pass-phrase). A “secure salt” is generated by scrambling the concatenation of a random phrases from the alphanumeric vocabulary.

Returned password size is 4*level except when a pass-phrase is given for level <= 4 where depend on the count of valid characters of pass-phrase argument, although minimum required is warranted. When pass-phrase is None for level zero or negative, size 4 is assumed. First four levels are considered weak.

Maximum size is defined in the MAX_PASSWORD_SIZE constant.

Default level is PASS_PHRASE_LEVEL_MAPPED_DATED when using a pass-phrase.

xoutil.crypto.PASS_PHRASE_LEVEL_BASIC = 0

The most basic level (less ) for the password generation.

xoutil.crypto.PASS_PHRASE_LEVEL_MAPPED = 1

A level for simply mapping of several chars.

xoutil.crypto.PASS_PHRASE_LEVEL_MAPPED_MIXED = 2

Another “stronger” mapping level.

xoutil.crypto.PASS_PHRASE_LEVEL_MAPPED_DATED = 3

Appends the year after mapping.

xoutil.crypto.PASS_PHRASE_LEVEL_STRICT = 4

Totally scramble the result, making very hard to predict the result.

xoutil.crypto.DEFAULT_PASS_PHRASE_LEVEL = 3

The default level for generate_password()

xoutil.crypto.MAX_PASSWORD_SIZE = 512

An upper limit for generated password length.

xoutil.data - Useful data-structures and data related algorithms

Some useful Data Structures and data-related algorithms and functions.

Deprecated since version 1.4.0: This module is completely deprecated since 1.4.0. Most of its contents are already deprecated.

xoutil.data.adapt_exception(value, **kwargs)[source]

Like PEP-246, Object Adaptation, with adapt(value, Exception, None).

If the value is not an exception is expected to be a tuple/list which contains an Exception type as its first item.

xoutil.datetime - Basic date and time types

Extends the standard datetime module.

  • Python’s datetime.strftime doesn’t handle dates previous to 1900. This module define classes to override date and datetime to support the formatting of a date through its full proleptic Gregorian date range.

Based on code submitted to comp.lang.python by Andrew Dalke, copied from Django and generalized.

You may use this module as a drop-in replacement of the standard library datetime module.

xoutil.datetime.new_date(*args, **kw)[source]

Generate a safe date from a legacy datetime date object.

xoutil.datetime.new_datetime(*args, **kw)[source]

Generate a safe datetime given a legacy date or datetime object.

xoutil.datetime.strfdelta(delta)[source]

Format a timedelta using a smart pretty algorithm.

Only two levels of values will be printed.

>>> def t(h, m):
...     return timedelta(hours=h, minutes=m)

>>> strfdelta(t(4, 56)) == '4h 56m'
True
xoutil.datetime.strftime(dt, fmt)[source]

Used as strftime method of date and datetime redefined classes.

Also could be used with standard instances.

xoutil.datetime.get_month_first(ref=None)[source]

Given a reference date, returns the first date of the same month. If ref is not given, then uses current date as the reference.

xoutil.datetime.get_month_last(ref=None)[source]

Given a reference date, returns the last date of the same month. If ref is not given, then uses current date as the reference.

xoutil.datetime.get_next_month(ref=None, lastday=False)[source]

Get the first or last day of the next month.

If lastday is False return the first date of the next month. Otherwise, return the last date.

The next month is computed with regards to a reference date. If ref is None, take the current date as the reference.

Examples:

>>> get_next_month(date(2017, 1, 23))
date(2017, 2, 1)
>>> get_next_month(date(2017, 1, 23), lastday=True)
date(2017, 2, 28)

New in version 1.7.3.

xoutil.datetime.is_full_month(start, end)[source]

Returns true if the arguments comprises a whole month.

xoutil.datetime.daterange([start, ]stop[, step])[source]

Similar to standard ‘range’ function, but for date objets.

Returns an iterator that yields each date in the range of [start, stop), not including the stop.

If start is given, it must be a date (or datetime) value; and in this case only stop may be an integer meaning the numbers of days to look ahead (or back if stop is negative).

If only stop is given, start will be the first day of stop’s month.

step, if given, should be a non-zero integer meaning the numbers of days to jump from one date to the next. It defaults to 1. If it’s positive then stop should happen after start, otherwise no dates will be yielded. If it’s negative stop should be before start.

As with range, stop is never included in the yielded dates.

class xoutil.datetime.TimeSpan(start_date=None, end_date=None)[source]

A continuous span of time.

Time spans objects are iterable. They yield exactly two times: first the start date, and then the end date:

>>> ts = TimeSpan('2017-08-01', '2017-09-01')
>>> tuple(ts)
(date(2017, 8, 1), date(2017, 9, 1))

Time spans objects have two items:

>>> ts[0]
date(2017, 8, 1)

>>> ts[1]
date(2017, 9, 1)

>>> ts[:]
(date(2017, 8, 1), date(2017, 9, 1))

Two time spans are equal if their start_date and end_date are equal. When comparing a time span with a date, the date is coerced to a time span (from_date()).

A time span with its start set to None is unbound to the past. A time span with its end set to None is unbound to the future. A time span that is both unbound to the past and the future contains all possible dates. A time span that is not unbound in any direction is bound .

A bound time span is valid if its start date comes before its end date.

Time spans can intersect, compared for containment of dates and by the subset/superset order operations (<=, >=). In this regard, they represent the set of dates between start and end, inclusively.

Warning

Time spans don’t implement the union or difference operations expected in sets because the difference/union of two span is not necessarily continuous.

classmethod from_date(date)[source]

Return a new time span that covers a single date.

past_unbound

True if the time span is not bound into the past.

future_unbound

True if the time span is not bound into the future.

unbound

True if the time span is unbound into the past or unbount into the future or both.

bound

True if the time span is not unbound.

valid

A bound time span is valid if it starts before it ends.

Unbound time spans are always valid.

__le__(other)[source]

True if other is a superset.

issubset()

An alias for __le__().

__ge__(other)[source]

True if other is a subset.

issuperset()

An alias for __ge__().

covers()

An alias for __ge__().

isdisjoint(other)[source]
overlaps(other)[source]

Test if the time spans overlaps.

__and__(other)[source]

Get the time span that is the intersection with another time span.

If two time spans don’t overlap, return the empty time span.

If other is not a TimeSpan we try to create one. If other is a date, we create the TimeSpan that starts and end that very day. Other types are passed unchanged to the constructor.

__mul__()

An alias for __and__().

intersection(*others)[source]

Return self [& other1 & ...].

EmptyTimeSpan

The empty time span. It’s not an instance of TimeSpan but engage set-like operations: union, intersection, etc.

No date is a member of the empty time span. The empty time span is a proper subset of any time span. It’s only a superset of itself. It’s not a proper superset of any other time span nor itself.

This instance is a singleton.

xoutil.decorator - Several decorators

This module contains several useful decorators, for several purposed. Also it severs as a namespace for other well-defined types of decorators.

Warning

This modules will be progressively deprecated during the 1.6 series.

We feel that either xoutil.objects or xoutil.functools are a better match for some of these decorators. But since we need to make sure about keeping dependencies, the deprecation won’t be final until 1.7.0. After 1.8.0, this modules will be finally removed.

Top-level decorators

class xoutil.decorator.AttributeAlias(attr_name)[source]

Descriptor to create aliases for object attributes.

This descriptor is mainly to be used internally by aliases() decorator.

xoutil.decorator.settle(**kwargs)[source]

Returns a decorator that sets different attribute values to the decorated target (function or class).

Usage:

>>> @settle(name='Name')
... class Person(object):
...    pass

>>> Person.name
'Name'
xoutil.decorator.namer(name, **kwargs)[source]

Like settle(), but name is a positional argument and is assigned to the attribute __name__.

Usage:

>>> @namer('Identity', custom=1)
... class I(object):
...    pass

>>> I.__name__
'Identity'

>>> I.custom
1
xoutil.decorator.aliases(*names, **kwargs)[source]

In a class, create an AttributeAlias descriptor for each definition as keyword argument (alias=existing_attribute).

If “names” are given, then the definition context is looked and are assigned to it the same decorator target with all new names:

>>> @aliases('foo', 'bar')
... def foobar(*args):
...     'This function is added to its module with two new names.'
xoutil.decorator.assignment_operator(func, maybe_inline=False)[source]

Makes a function that receives a name, and other args to get its first argument (the name) from an assigment operation, meaning that it if its used in a single assignment statement the name will be taken from the left part of the = operator.

Warning

This function is dependant of CPython’s implementation of the language and won’t probably work on other implementations. Use only you don’t care about portability, but use sparingly (in case you change your mind about portability).

xoutil.decorator.instantiate(target, *args, **kwargs)[source]

Some singleton classes must be instantiated as part of its declaration because they represents singleton objects.

Every argument, positional or keyword, is passed as such when invoking the target. The following two code samples show two cases:

>>> @instantiate
... class Foobar(object):
...    def __init__(self):
...        print('Init...')
Init...


>>> @instantiate('test', context={'x': 1})
... class Foobar(object):
...    def __init__(self, name, context):
...        print('Initializing a Foobar instance with name={name!r} '
...              'and context={context!r}'.format(**locals()))
Initializing a Foobar instance with name='test' and context={'x': 1}

In all cases, Foobar remains the class, not the instance:

>>> Foobar  
<class '...Foobar'>
class xoutil.decorator.memoized_property(fget, doc=None)[source]

A read-only property that is only evaluated once.

This is extracted from the SQLAlchemy project’s codebase, merit and copyright goes to SQLAlchemy authors:

Copyright (C) 2005-2011 the SQLAlchemy authors and contributors

This module is part of SQLAlchemy and is released under the MIT License:
http://www.opensource.org/licenses/mit-license.php
class xoutil.decorator.memoized_instancemethod(fget, doc=None)[source]

Decorate a method memoize its return value.

Best applied to no-arg methods: memoization is not sensitive to argument values, and will always return the same value even when called with different arguments.

This is extracted from the SQLAlchemy project’s codebase, merit and copyright goes to SQLAlchemy authors:

Copyright (C) 2005-2011 the SQLAlchemy authors and contributors

This module is part of SQLAlchemy and is released under the MIT License:
http://www.opensource.org/licenses/mit-license.php

Sub packages

xoutil.decorator.development - Decorators for development annotations
xoutil.decorator.development.unstable(target, msg=None)[source]

Declares that a method, class or interface is unstable.

This has the side-effect of issuing a warning the first time the target is invoked.

The msg parameter, if given, should be string that contains, at most, two positional replacement fields ({0} and {1}). The first replacement field will be the type of target (interface, class or function) and the second matches target’s full name.

xoutil.decorator.meta - Decorator-making facilities

Decorator-making facilities.

This module provides a signature-keeping version of the xoutil.decorators.decorator(), which is now deprecated in favor of this module’s version.

We scinded the decorator-making facilities from decorators per se to allow the module xoutil.deprecation to be used by decorators and at the same time, implement the decorator deprecated() more easily.

This module is an adapted work from the decorator version 3.3.2 package and is copyright of its owner as stated below. Adaptation work is done by Merchise.

Original copyright and license notices from decorator package:

Copyright (c) 2005-2011, Michele Simionato

All rights reserved.

Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in bytecode form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

class xoutil.decorator.meta.FunctionMaker(func=None, name=None, signature=None, defaults=None, doc=None, module=None, funcdict=None)[source]

An object with the ability to create functions with a given signature. It has attributes name, doc, module, signature, defaults, dict and methods update and make.

classmethod create(obj, body, evaldict, defaults=None, doc=None, module=None, addsource=True, **attrs)[source]

Create a function from the strings name, signature and body. “evaldict” is the evaluation dictionary. If addsource is true an attribute __source__ is added to the result. The attributes attrs are added, if any.

make(src_templ, evaldict=None, addsource=False, **attrs)[source]

Make a new function from a given template and update the signature

update(func, **kw)[source]

Update the signature of func with the data in self

xoutil.decorator.meta.flat_decorator(caller, func=None)[source]

Creates a signature keeping decorator.

decorator(caller) converts a caller function into a decorator.

decorator(caller, func) decorates a function using a caller.

xoutil.decorator.meta.decorator(caller)[source]

Eases the creation of decorators with arguments. Normally a decorator with arguments needs three nested functions like this:

def decorator(*decorator_arguments):
    def real_decorator(target):
        def inner(*args, **kwargs):
            return target(*args, **kwargs)
        return inner
    return real_decorator

This decorator reduces the need of the first level by comprising both into a single function definition. However it does not removes the need for an inner function:

>>> @decorator
... def plus(target, value):
...    from functools import wraps
...    @wraps(target)
...    def inner(*args):
...        return target(*args) + value
...    return inner

>>> @plus(10)
... def ident(val):
...     return val

>>> ident(1)
11

A decorator with default values for all its arguments (except, of course, the first one which is the decorated target) may be invoked without parenthesis:

>>> @decorator
... def plus2(func, value=1, missing=2):
...    from functools import wraps
...    @wraps(func)
...    def inner(*args):
...        print(missing)
...        return func(*args) + value
...    return inner

>>> @plus2
... def ident2(val):
...     return val

>>> ident2(10)
2
11

But (if you like) you may place the parenthesis:

>>> @plus2()
... def ident3(val):
...     return val

>>> ident3(10)
2
11

However, this is not for free, you cannot pass a single positional argument which type is a function:

>>> def p():
...    print('This is p!!!')

>>> @plus2(p)   
... def dummy():
...    print('This is dummy')
Traceback (most recent call last):
    ...
TypeError: p() takes ...

The workaround for this case is to use a keyword argument.

xoutil.deprecation - Utils for marking deprecated elements

xoutil.deprecation.deprecated(replacement, msg=None, deprecated_module=None, removed_in_version=None, check_version=False)[source]

Small decorator for deprecated functions.

Usage:

@deprecate(new_function)
def deprecated_function(...):
    ...
Parameters:
  • replacement – Either a string or the object that replaces the deprecated.
  • msg

    A deprecation warning message template. You should provide keyword arguments for the format() function. Currently we pass the current keyword arguments: replacement (after some processing), funcname with the name of the currently deprecated object and in_version with the version this object is going to be removed if removed_in_version argument is not None.

    Defaults to: “{funcname} is now deprecated and it will be removed{in_version}. Use {replacement} instead.”

  • removed_in_version – The version the deprecated object is going to be removed.
  • check_version

    If True and removed_in_version is not None, then declarations of obseleted objects will raise a DeprecationError. This helps the release manager to keep the release clean.

    Note

    Currently only works with setuptools’ installed distributions.

  • deprecated_module – If provided, the name of the module the deprecated object resides. Not needed if the deprecated object is a function or class.

Changed in version 1.4.1: Introduces removed_in_version and check_version.

xoutil.dim - Facilities to work with concrete numbers

The name ‘dim’ is a short of dimension. We borrow it from the topic dimensional analysis, even though the scope of this module is less ambitious.

This module is divided in two major parts: meta-definitions and applications.

xoutil.dim.meta – Meta-definitions for concrete numbers.

Facilities to work with concrete numbers.

A concrete number is a number associated with the things being counted, in contrast to an abstract number which is a number as a single entity.

Wikipedia

This module allows you to define dimensions (or quantity types):

>>> from xoutil.dim.meta import Dimension, UNIT
>>> @Dimension.new
... class Length(object):
...     metre = UNIT
...     kilometre = 1000 * metre
...     centimetre = metre/100
...     milimetre = milimetres = metre/1000
...
...     inch = inches = 24.5 * milimetres
...     foot = feet = 12 * inches

See also

Module base defines the standard base quantities.

Each dimension must define a single canonical unit for measuring quantities within the dimension. Values in the dimension are always expressed in terms of the canonical units.

In the previous example the dimension Length defined the metre for its canonical unit. The name of canonical unit defines the signature for the quantities in the dimension.

When printed (or repr-ed) quantities use the format <magnitude>::<signature>:

>>> metre = Length.metre
>>> metre
1::{<Length.metre>}/{}

Quantities support the standard arithmetical operations of addition, subtraction, multiplication and division. In fact, you obtain different quantities in the dimension by multiplying with the canonical unit:

>>> metre + metre
2::{<Length.metre>}/{}
>>> metre*metre
1::{<Length.metre>, <Length.metre>}/{}
>>> km = 1000 * metre
>>> 5 * km
5000::{<Length.metre>}/{}

Dimensional homogeneity imposes restrictions on the allowed operations between quantities. Only commensurable quantities (quantities of the same dimension) can be compared, equated, added, or subtracted.

>>> @Dimension.new
>>> class Time(object):
...     second = UNIT
>>> metre + Time.second  
Traceback (...)
...
OperandTypeError: unsupported operand type(s) for +:...

However, you can take ratios of incommensurable quantities (quantities with different dimensions), and multiply or divide them.

>>> metre/Time.second
>>> 1::{<Length.metre>}/{<Time.second>}

Warning

Decimal numbers are not supported.

This module makes not attempt to fix the standing incompatibility between floats and decimal.Decimal:

>>> import decimal
>>> decimal.Decimal('0') + 0.1  
Traceback (...)
...
TypeError: unsupported operand type(s) for +: 'Decimal' and 'float'

The signature created by Dimension for its canonical unit is simply a string that varies with the name of the dimension and that of the canonical unit. This implies that you can recreate the same dimension and it will be interoperable with the former:

>>> @Dimension.new
... class L(object):
...    m = UNIT

>>> m = L.m  # Save this


>>> # Recreate the same dimension.
>>> @Dimension.new
... class L(object):
...    m = UNIT

>>> m == L.m
True

Both the dimension name and the canonical unit name must be the same for this to work.

Note

We advice to define a dimension only once and import it where needed.

class xoutil.dim.meta.Dimension[source]

A type for quantities.

This is a metaclass for dimensions. Every instance (class) will automatically have the following attributes:

_unitname_

The name of canonical unit in the dimension. Notice that aliases are created after the defined canonical unit. This is the name of the attribute provided in the class definition of the dimension with value equal to UNIT.

_unit_

The canonical quantity. This is the quantity 1 (UNIT) expressed in terms of the canonical unit.

_signature_

The canonical signature of the quantities.

It’s always true that Quantity(UNIT, self._signature_) == self._unit_.

The provided dimension Length has the canonical quantity 1 metre:

>>> Length.metre
1::{<Length.metre>}/{}

>>> Length._unit_ == Length.metre == Quantity(1, Length._signature_)
True
classmethod new(*source, **kwargs)[source]

Define a new dimension.

This is a wrapped decorator. The actual possible signatures are:

  • new(unit_alias=None, unit_aliases=None)(source)
  • new(source)

This allows to use this method as decorator with or without arguments.

Parameters:
  • source – A class with at least the canonical unit definition. Other unit definitions will be automatically converted.
  • unit_alias – An alias for the canonical unit. You cannot use a source with several canonical units. This is a simple way to introduce a single alias.
  • unit_aliases – A sequence with the name of other aliases for the canonical unit.

Example:

>>> @Dimension.new(unit_alias='man')
... class Workforce(object):
...    men = UNIT
>>> Workforce.men == Workforce.man == Workforce._unit_
True

The resulting class will be an instance of Dimension

>>> isinstance(Workforce, Dimension)
True

The original class is totally missed:

>>> Workforce.mro()
[...Workforce, object]

To complete the example, let’s introduce the dimension Effort that expresses the usual amount of men-power and time needed to complete some task. However Time has the second as it canonical unit, but the standard for Effort is men-hour:

>>> class Effort(Workforce * Time):
...    # Since the canonical unit of a composed quantity type is built from
...    # the canonical units of the operands, but the true "canonical type"
...    # of effort is usually men-hour we re-introduce it.
...    men_hour = 60

This does not mean that Effort._unit_ == Effort.men_hour. The canonical unit would be Effort.men_second.

class xoutil.dim.meta.Signature(top=None, bottom=None)[source]

The layout of the kinds that compose a quantity.

The layout is given by a pair non-ordered collections (repetition is allowed): the numerator (we call it top within the signature) and the denominator (bottom).

We represent a signature as {top elements}/{bottom elements}.

You may regard a signature as an abstract ‘syntactical’ part of a quantity. For Length, the {metre}/{} is the signature of such a quantity.

The number “10” is not tied to any particular kind of quantity. Bare numbers have no kind and the bear the signature {}/{}.

The items of top and bottom are required to be comparable for equality (==).

You can multiply and divide signatures and simplification happens automatically.

You should regard signatures as immutable values. In fact, this is kind of an internal, but interesting, concept of this module.

Examples:

>>> distance = Signature('m')
>>> distance
{m}/{}

>>> time = Signature('s')

>>> freq = 1/time
>>> freq
{}/{s}

>>> speed = distance/time
>>> speed
{m}/{s}

>>> acceleration = speed/time
>>> acceleration
{m}/{s, s}

You may compare signatures for equality.

>>> acceleration == distance/(time*Signature('s'))
True
>>> speed == distance * freq
True

Signature don’t support neither addition nor subtraction:

>>> distance + distance  
Traceback (...)
...
TypeError: unsupported operand type(s) for +: 'Signature' and 'Signature'
isunit(*args, **kw)[source]

Return True is this is the signature of the UNIT.

Changed in version 1.7.9: Deprecated. This is the same as comparing with SCALAR.

static simplify(top, bottom)[source]

Removes equal items from top and bottom in a one-to-one correspondence.

Signatures are simplified on initialization:

>>> Signature('abcxa', 'bxay')
{c, a}/{y}

This function takes top and bottom and returns simplified tuples for top and bottom.

class xoutil.dim.meta.Quantity(quantity, units)[source]

A concrete number of quantity (expressed in) units.

Parameters:
  • quantity – A real number.
  • units – A signature for the units the denominate the given quantity.

You can construct instances by operating with the attributes of a dimension. For instance, this is 5 kilometres:

>>> from xoutil.dim.base import L
>>> 5 * L.km
5000::{<Length.metre>}/{}

A concrete number is of the type of its dimension:

>>> isinstance(5 * L.km, L)
True
class xoutil.dim.meta.Scalar[source]

A quantity whose signature is always empty.

Most of the time you should not deal with this quantity. Any normal operation that results in a scalar gets reduced to Python’s type:

>>> from xoutil.dim.base import L
>>> L.m/L.m
1.0

This type makes the operations on dimensions closed under multiplication:

>>> Scalar * L == L == L * Scalar
True
xoutil.dim.meta.UNIT

This the constant value 1. It’s given this name to emphasize it’s the canonical unit for a dimension.

xoutil.dim.meta.SCALAR

The signature of dimensionless quantities.

class xoutil.dim.meta.QuantityType[source]

Deprecated alias for Dimension.

Changed in version 1.7.9: Deprecated.

xoutil.dim.base - The base physical quantities

The standard physical quantities.

class xoutil.dim.base.Length[source]

The Length base quantity.

metre

The canonical unit.

m

An alias of metre

Other attributes:

kilometre
km
centimetre
cm
millimetre
mm
nanometre
nm
class xoutil.dim.base.Time[source]

The Time base quantity.

second

The canonical unit.

s

An alias of second

Other attributes:

millisecond
ms
nanosecond
ns
minute
hour
class xoutil.dim.base.Mass[source]

The Mass base quantity.

kilogram

The canonical unit.

kg

An alias of kilogram

Other attributes:

gram
class xoutil.dim.base.ElectricCurrent[source]

The electrical current base quantity.

ampere

The canonical unit.

A

An alias of ampere

class xoutil.dim.base.Temperature[source]

The thermodynamic temperature base quantity.

kelvin

The canonical unit.

K

An alias of kelvin

classmethod from_celcius(val)[source]

Convert val ºC to K

classmethod from_fahrenheit(val)[source]

Convert val ºF to K

class xoutil.dim.base.Substance[source]

The amount of substance.

mole
mol

An alias of mole.

class xoutil.dim.base.Luminosity[source]

The luminous intensity base quantity.

candela
Aliases
class xoutil.dim.base.L

An alias of Length

class xoutil.dim.base.T

An alias of Time

class xoutil.dim.base.M

An alias of Mass

class xoutil.dim.base.I

An alias of ElectricCurrent

class xoutil.dim.base.O

An alias of Temperature. We can’t really use the Greek Theta Θ

class xoutil.dim.base.N

An alias of Substance

class xoutil.dim.base.J

An alias of Luminosity

Derived quantities
class xoutil.dim.base.Area

Defined as L**2.

metre_squared

The canonical unit.

class xoutil.dim.base.Volume

Defined as L**3.

metre_cubic

The canonical unit.

class xoutil.dim.base.Frequency

Defined as T**-1 (which is the same as 1/T).

unit_per_second

The canonical unit.

Aliases of the canonical unit:

Hz
class xoutil.dim.base.Force

Defined as L * M * T**-2.

metre_kilogram_per_second_squared

The canonical unit.

Aliases of the canonical unit:

N
Newton
class xoutil.dim.base.Presure

Defined as L**-1 * M * T**-2.

kilogram_per_metre_per_second_squared

Aliases of the canonical unit:

pascal
Pa
class xoutil.dim.base.Velocity

Defined as L * T**-1.

metre_per_second

The canonical unit.

class xoutil.dim.base.Acceleration

Defined as L * T**-2.

metre_per_second_squared

The canonical unit.

On the automatically created names for derived quantities

We automatically create the name of the canonical unit of quantities derived from others by simple rules:

  • A * B joins the canonical unit names together with a low dash ‘_’ in-between. Let’s represent it as a_b, where a stands for the name of the canonical unit of A and b, the canonical unit of B.

    For the case, A * A the unit name is a_squared.

  • A/B gets the name a_per_b. 1/A gets the name unit_per_a

  • A**n; when n=1 this is the same as A; when n=2 this is the same as A * A; for other positive values of n, the canonical unit name is a_pow_n; for negative values of n is the same as 1/A**n; for n=0 this is the Scalar quantity.

xoutil.dim.currencies – Concrete numbers for money

Concrete numbers for money.

You may have 10 dollars and 5 euros in your wallet, that does not mean that you have 15 of anything (but bills, perhaps). Though you may evaluate your cash in any other currency you don’t have that value until you perform an exchange with a given rate.

This module support the family of currencies. Usage:

>>> from xoutil.dim.currencies import Rate, Valuation, currency
>>> dollar = USD = currency('USD')
>>> euro = EUR = currency('EUR')
>>> rate = 1.19196 * USD/EUR

>>> isinstance(dollar, Valuation)
True

>>> isinstance(rate, Rate)
True

# Even 0 dollars are a valuation
>>> isinstance(dollar - dollar, Valuation)
True

# But 1 is not a value nor a rate
>>> isinstance(dollar/dollar, Valuation) or isinstance(dollar/dollar, Rate)
False

Currency names are case-insensitive. We don’t check the currency name is listed in ISO 4217. So currency MVA is totally acceptable in this module.

We don’t download rates from any source.

This module allows you to trust your computations of money by allowing only sensible operations:

>>> dollar + euro  
Traceback (...)
...
OperandTypeError: unsupported operand type(s) for +: '{USD}/{}' and '{EUR}/{}

If you convert your euros to dollars:

>>> dollar + rate * euro
2.19196::{USD}/{}

# Or your dollars to euros
>>> dollar/rate + euro
1.83895432733::{EUR}/{}

xoutil.eight – Extensions for writing code that runs on Python 2 and 3

Xoutil extensions for writing code that runs on Python 2 and 3

The name comes from (Manu’s idea’) “2 raised to the power of 3”.

There is an existing library written by “Benjamin Peterson” named six, both (xoutil.eight and six) can be used together since this module don’t claim to be a replacement of six, just some extra extensions. Nevertheless, there are some simple definitions that even when are in six also are defined also here.

This package also fixes some issues from PyPy interpreter.

xoutil.eight.exec_(_code_, _globs_=None, _locs_=None)[source]

Execute code in a namespace.

xoutil.eight.intern(string) → string[source]

``Intern’’ the given string. This enters the string in the (global) table of interned strings whose purpose is to speed up dictionary lookups. Return the string itself or the previously interned string object with the same value.

xoutil.eight.iteritems(d)[source]

Return an iterator over the (key, value) pairs of a dictionary.

xoutil.eight.iterkeys(d)[source]

Return an iterator over the keys of a dictionary.

xoutil.eight.itervalues(d)[source]

Return an iterator over the values of a dictionary.

xoutil.eight.typeof(obj)[source]

Obtain the object’s type compatible with Py 2**3.

Tools

Implements the metaclass() function using the Py3k syntax.

xoutil.eight.meta.metaclass(meta, **kwargs)[source]

Define the metaclass of a class.

New in version 1.7.0: It’s available as xoutil.objects.metaclass() since 1.4.1. That alias is now deprecated and will be removed in 1.8.

This function allows to define the metaclass of a class equally in Python 2 and 3.

Usage:

>>> class Meta(type):
...   pass

>>> class Foobar(metaclass(Meta)):
...   pass

>>> class Spam(metaclass(Meta), dict):
...   pass

>>> type(Spam) is Meta
True

>>> Spam.__bases__ == (dict, )
True

New in version 1.5.5: The kwargs keywords arguments with support for __prepare__.

Metaclasses are allowed to have a __prepare__ classmethod to return the namespace into which the body of the class should be evaluated. See PEP 3115.

Warning

The PEP 3115 is not possible to implement in Python 2.7.

Despite our best efforts to have a truly compatible way of creating meta classes in both Python 2.7 and 3.0+, there is an inescapable difference in Python 2.7. The PEP 3115 states that __prepare__ should be called before evaluating the body of the class. This is not possible in Python 2.7, since __new__ already receives the attributes collected in the body of the class. So it’s always too late to call __prepare__ at this point and the Python 2.7 interpreter does not call it.

Our approach for Python 2.7 is calling it inside the __new__ of a “side” metaclass that is used for the base class returned. This means that __prepare__ is called only for classes that use the metaclass() directly. In the following hierarchy:

class Meta(type):
     @classmethod
     def __prepare__(cls, name, bases, **kwargs):
         from xoutil.collections import OrderedDict
         return OrderedDict()

class Foo(metaclass(Meta)):
     pass

class Bar(Foo):
     pass

when creating the class Bar the __prepare__() class method is not called in Python 2.7!

Warning

You should always place your metaclass declaration first in the list of bases. Doing otherwise triggers twice the metaclass’ constructors in Python 3.1 or less.

If your metaclass has some non-idempotent side-effect (such as registration of classes), then this would lead to unwanted double registration of the class:

>>> class BaseMeta(type):
...     classes = []
...     def __new__(cls, name, bases, attrs):
...         res = super(BaseMeta, cls).__new__(cls, name, bases, attrs)
...         cls.classes.append(res)   # <-- side effect
...         return res

>>> class Base(metaclass(BaseMeta)):
...     pass

>>> class SubType(BaseMeta):
...     pass

>>> class Egg(metaclass(SubType), Base):   # <-- metaclass first
...     pass

>>> Egg.__base__ is Base   # <-- but the base is Base
True

>>> len(BaseMeta.classes) == 2
True

>>> class Spam(Base, metaclass(SubType)):
...     'Like "Egg" but it will be registered twice in Python 2.x.'

In this case the registration of Spam ocurred twice:

>>> BaseMeta.classes  
[<class Base>, <class Egg>, <class Spam>, <class Spam>]

Bases, however, are just fine:

>>> Spam.__bases__ == (Base, )
True

New in version 1.7.1: Now are accepted atypical meta-classes, for example functions or any callable with the same arguments as those that type accepts (class name, tuple of base classes, attributes mapping).

All functions implemented in module types in Python 3 but not in Python 2.

xoutil.eight.types.MemberDescriptorType

alias of member_descriptor

class xoutil.eight.types.SimpleNamespace(**kwargs)[source]

A simple attribute-based namespace.

SimpleNamespace(**kwargs)

class xoutil.eight.types.DynamicClassAttribute(fget=None, fset=None, fdel=None, doc=None)[source]

Route attribute access on a class to __getattr__().

This is a descriptor, used to define attributes that act differently when accessed through an instance and through a class. Instance access remains normal, but access to an attribute through a class will be routed to the class’s __getattr__() method; this is done by raising AttributeError.

This allows one to have properties active on an instance, and have virtual attributes on the class with the same name (see Enum for an example).

New in version 1.5.5.

Note

The class Enum mentioned has not yet been backported.

Note

In Python 3.4+ this is an alias to types.DynamicClassAttribute.

xoutil.eight.types.new_class(name, bases=(), kwds=None, exec_body=None)[source]

Create a class object dynamically using the appropriate metaclass.

xoutil.eight.types.prepare_class(name, bases=(), kwds=None)[source]

Call the __prepare__ method of the appropriate metaclass.

Returns (metaclass, namespace, kwds) as a 3-tuple

metaclass is the appropriate metaclass namespace is the prepared class namespace kwds is an updated copy of the passed in kwds argument with any ‘metaclass’ entry removed. If no kwds argument is passed in, this will be an empty dict.

Extensions to Python’s io module.

You may use it as drop-in replacement of io. Although we don’t document all items here. Refer to io documentation.

In Python 2, buil-int open() is different from io.open(); in Python 3 are the same function.

So, generated files with the built-in funtion in Python 2, can not be processed using abc types, for example:

f = open('test.rst')
assert isinstance(f, io.IOBase)

will fail in Python 2 and not in Python 3.

Another incompatibilities:

  • file type doesn’t exists in Python 3.
  • Python 2 instances created with io.StringIO:class`, or with io.open() using text mode, don’t accept str values, so it will be better to use any of the standards classes (StringIO.StringIO, cStringIO.StringIO or open() built-in).

New in version 1.7.0.

xoutil.eight.io.is_file_like(obj)[source]

Return if obj is a valid file type or not.

Compatibility exceptions between Python 2 and 3.

Python 2 defines an module named exceptions but Python 3 doesn’t. Also, there are some exception classes defined in Python 2 but not in Python 3.

xoutil.eight.exceptions.reraise(tp, value, tb=None)[source]

Raise an exception with a traceback.

In Python 2.7 this is the same as:

raise T, V, tb

In Python 3+ this is:

raise T(V).with_traceback(tb)

“”“Abstract Base Classes (ABCs) according to PEP 3119.”“”

Compatibility module between Python 2 and 3.

This module defines one symbol that is defined in Python 3 as a class:

class ABC(metaclass=ABCMeta):
“”“Helper class that provides a standard way to create an ABC using inheritance. “”” pass

In our case it’s defined as ABC = metaclass(ABCMeta), that is a little tricky (see xoutil.eight.meta.metaclass:func`).

abstractclassmethod is deprecated. Use classmethod with abstractmethod instead.

abstractstaticmethod is deprecated. Use staticmethod with abstractmethod instead.

xoutil.formatter - Formatting

Smart formatting.

class xoutil.formatter.Template(template)[source]

A string class for supporting $-substitutions.

It has similar interface that string.Template but using “eval” instead simple dictionary looking.

This means that you get all the functionality provided by string.Template (although, perhaps modified) and you get also the ability to write more complex expressions.

If you need repetition or other flow-control sentences you should use other templating system.

If you enclose and expression within ${?...} it will be evaluated as a python expression. Simple variables are allowed just with $var or ${var}:

>>> tpl = Template(str('${?1 + 1} is 2, and ${?x + x} is $x + ${x}'))
>>> (tpl % dict(x=4)) == '2 is 2, and 8 is 4 + 4'
True

The mapping may be given by calling the template:

>>> tpl(x=5) == '2 is 2, and 10 is 5 + 5'
True
xoutil.formatter.count(source, chars)[source]

Counts how chars from chars are found in source:

>>> count('Todos los nenes del mundo vamos una rueda a hacer', 'a')
1

# The vowel "i" is missing
>>> count('Todos los nenes del mundo vamos una rueda a hacer', 'aeiuo')
4

xoutil.fs – file system utilities

File system utilities.

This module contains file-system utilities that could have side-effects. For path-handling functions that have no side-effects look at xoutil.fs.path.

xoutil.fs.ensure_filename(filename, yields=False)[source]

Ensures the existence of a file with a given filename.

If the filename is taken and is not pointing to a file (or a link to a file) an OSError is raised. If exist_ok is False the filename must not be taken; an OSError is raised otherwise.

The function creates all directories if needed. See makedirs() for restrictions.

If yields is True, returns the file object. This way you may open a file for writing like this:

with ensure_filename('/tmp/good-name-87.txt', yields=True) as fh:
    fh.write('Do it!')

The file is open in mode ‘w+b’.

New in version 1.6.1: Added parameter yield.

xoutil.fs.imap(func, pattern)[source]

Yields func(file_0, stat_0), func(file_1, stat_1), ... for each dir path. The pattern may contain:

  • Simple shell-style wild-cards à la fnmatch.
  • Regex if pattern starts with ‘(?’. Expressions must be valid, as in “(?:[^.].*)$” or “(?i).*.jpe?g$”. Remember to add the end mark ‘$’ if needed.
xoutil.fs.iter_dirs(top=u'.', pattern=None, regex_pattern=None, shell_pattern=None)[source]

Iterate directories recursively.

The params have analagous meaning that in iter_files() and the same restrictions.

xoutil.fs.iter_files(top=u'.', pattern=None, regex_pattern=None, shell_pattern=None, followlinks=False, maxdepth=None)[source]

Iterate filenames recursively.

Parameters:
  • top – The top directory for recurse into.
  • pattern – A pattern of the files you want to get from the iterator. It should be a string. If it starts with “(?” it will be regarded as a regular expression, otherwise a shell pattern.
  • regex_pattern – An alternative to pattern. This will always be regarded as a regular expression.
  • shell_pattern – An alternative to pattern. This should be a shell pattern.
  • followlinks

    The same meaning that in os.walk.

    New in version 1.2.1.

  • maxdepth

    Only files above this level will be yielded. If None, no limit is placed.

    New in version 1.2.1.

Warning

It’s an error to pass more than pattern argument.

xoutil.fs.listdir(path)[source]

Same as os.listdir but normalizes path and raises no error.

xoutil.fs.rmdirs(top=u'.', pattern=None, regex_pattern=None, shell_pattern=None, exclude=None, confirm=None)[source]

Removes all empty dirs at top.

Parameters:
  • top – The top directory to recurse into.
  • pattern – A pattern of the dirs you want to remove. It should be a string. If it starts with “(?” it will be regarded as a regular expression, otherwise a shell pattern.
  • exclude – A pattern of the dirs you DON’T want to remove. It should be a string. If it starts with “(?” it will be regarded as a regular expression, otherwise a shell pattern. This is a simple commodity to have you not to negate complex patterns.
  • regex_pattern – An alternative to pattern. This will always be regarded as a regular expression.
  • shell_pattern – An alternative to pattern. This should be a shell pattern.
  • confirm – A callable that accepts a single argument, which is the path of the directory to be deleted. confirm should return True to allow the directory to be deleted. If confirm is None, then all matched dirs are deleted.

Note

In order to avoid common mistakes we won’t attempt to remove mount points.

New in version 1.1.3.

xoutil.fs.stat(path)[source]

Return file or file system status.

This is the same as the function os.stat but raises no error.

xoutil.fs.walk_up(start, sentinel)[source]

Given a start directory walk-up the file system tree until either the FS root is reached or the sentinel is found.

The sentinel must be a string containing the file name to be found.

Warning

If sentinel is an absolute path that exists this will return start, no matter what start is (in windows this could be even different drives).

If start path exists but is not a directory an OSError is raised.

xoutil.fs.concatfiles(*files, target)[source]

Concat several files to a single one.

Each positional argument must be either:

The last positional argument is the target. If it’s file-like object it must be open for writing, and the caller is the responsible for closing it.

Alternatively if there are only two positional arguments and the first is a collection, the sources will be the members of the first argument.

xoutil.fs.makedirs(path, mode=0o777, exist_ok=False)[source]

Recursive directory creation function. Like os.mkdir(), but makes all intermediate-level directories needed to contain the leaf directory.

The default mode is 0o777 (octal). On some systems, mode is ignored. Where it is used, the current umask value is first masked out.

If exist_ok is False (the default), an OSError is raised if the target directory already exists.

Note

makedirs() will become confused if the path elements to create include os.pardir (eg. ”..” on UNIX systems).

This function handles UNC paths correctly.

Changed in version 1.6.1: Behaves as Python 3.4.1.

Before Python 3.4.1 (ie. xoutil 1.6.1), if exist_ok was True and the directory existed, makedirs() would still raise an error if mode did not match the mode of the existing directory. Since this behavior was impossible to implement safely, it was removed in Python 3.4.1. See the original os.makedirs().

Contents:

xoutil.fs.path – Path utilities

Extensions to os.path

Functions inside this module must not have side-effects on the file-system. This module re-exports (without change) several functions from the os.path standard module.

xoutil.fs.path.join(base, *extras)[source]

Join two or more pathname components, inserting ‘/’ as needed.

If any component is an absolute path, all previous path components will be discarded.

Normalize path (after join parts), eliminating double slashes, etc.

xoutil.fs.path.fix_encoding(name, encoding=None)[source]

Fix encoding of a file system resource name.

encoding is ignored if name is already a str.

xoutil.fs.path.normalize_path(base, *extras)[source]

Normalize path by:

  • expanding ‘~’ and ‘~user’ constructions.
  • eliminating double slashes
  • converting to absolute.
xoutil.fs.path.shorten_module_filename(filename)[source]

A filename, normally a module o package name, is shortened looking his head in all python path.

xoutil.fs.path.shorten_user(filename)[source]

A filename is shortened looking for the (expantion) $HOME in his head and replacing it by ‘~’.

xoutil.fs.path.rtrim(path, n=1)

Trims the last n components of the pathname path.

This basically applies n times the function os.path.dirname to path.

path is normalized before proceeding (but not tested to exists).

Changed in version 1.5.5: n defaults to 1. In this case rtrim is identical to os.path.dirname().

Example:

>>> rtrim('/tmp/a/b/c/d', 3)
'/tmp/a'

# It does not matter if `/` is at the end
>>> rtrim('/tmp/a/b/c/d/', 3)
'/tmp/a'

xoutil.functools - Higher-order functions and operations on callable objects

Extensions to the functools module from the Python’s standard library.

You may use this module as drop-in replacement of functools.

xoutil.functools.lru_cache(maxsize=128, typed=False)[source]

Decorator to wrap a function with a memoizing callable that saves up to the maxsize most recent calls. It can save time when an expensive or I/O bound function is periodically called with the same arguments.

Since a dictionary is used to cache results, the positional and keyword arguments to the function must be hashable.

If maxsize is set to None, the LRU feature is disabled and the cache can grow without bound. The LRU feature performs best when maxsize is a power-of-two.

If typed is set to True, function arguments of different types will be cached separately. For example, f(3) and f(3.0) will be treated as distinct calls with distinct results.

To help measure the effectiveness of the cache and tune the maxsize parameter, the wrapped function is instrumented with a cache_info() function that returns a named tuple showing hits, misses, maxsize and currsize. In a multi-threaded environment, the hits and misses are approximate.

The decorator also provides a cache_clear() function for clearing or invalidating the cache.

The original underlying function is accessible through the __wrapped__ attribute. This is useful for introspection, for bypassing the cache, or for rewrapping the function with a different cache.

An LRU (least recently used) cache works best when the most recent calls are the best predictors of upcoming calls (for example, the most popular articles on a news server tend to change each day). The cache’s size limit assures that the cache does not grow without bound on long-running processes such as web servers.

xoutil.functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)[source]

Update a wrapper function to look like the wrapped function. The optional arguments are tuples to specify which attributes of the original function are assigned directly to the matching attributes on the wrapper function and which attributes of the wrapper function are updated with the corresponding attributes from the original function. The default values for these arguments are the module level constants WRAPPER_ASSIGNMENTS (which assigns to the wrapper function’s __name__, __module__, __annotations__ and __doc__, the documentation string) and WRAPPER_UPDATES (which updates the wrapper function’s __dict__, i.e. the instance dictionary).

To allow access to the original function for introspection and other purposes (e.g. bypassing a caching decorator such as lru_cache()), this function automatically adds a __wrapped__ attribute to the wrapper that refers to the original function.

The main intended use for this function is in decorator functions which wrap the decorated function and return the wrapper. If the wrapper function is not updated, the metadata of the returned function will reflect the wrapper definition rather than the original function definition, which is typically less than helpful.

update_wrapper() may be used with callables other than functions. Any attributes named in assigned or updated that are missing from the object being wrapped are ignored (i.e. this function will not attempt to set them on the wrapper function). AttributeError is still raised if the wrapper function itself is missing any attributes named in updated.

xoutil.functools.compose(*funcs, math=True)[source]

Returns a function that is the composition of several callables.

By default compose behaves like mathematical function composition: this is to say that compose(f1, ... fn) is equivalent to lambda _x: fn(...(f1(_x))...).

If any “intermediate” function returns a ctuple it is expanded as several positional arguments to the next function.

Changed in version 1.5.5: At least a callable must be passed, otherwise a TypeError is raised. If a single callable is passed it is returned without change.

Parameters:math – Indicates if compose should behave like mathematical function composition: last function in funcs is applied last. If False, then the last function in func is applied first.
xoutil.functools.power(*funcs, times)[source]

Returns the “power” composition of several functions.

Examples:

>>> import operator
>>> f = power(partial(operator.mul, 3), 3)
>>> f(23) == 3*(3*(3*23))
True

>>> power(operator.neg)
Traceback (most recent call last):
...
TypeError: Function `power` requires at least two arguments
class xoutil.functools.ctuple[source]

Simple tuple marker for compose().

Since is a callable you may use it directly in compose instead of changing your functions to returns ctuples instead of tuples:

>>> def compat_print(*args):
...     for arg in args:
...         print(arg)

>>> compose(compat_print, ctuple, list, range, math=False)(3)
0
1
2

# Without ctuple prints the list
>>> compose(compat_print, list, range, math=False)(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
class xoutil.functools.lwraps(f, n, *, name=None, doc=None, wrapped=None)[source]

Lambda wrapper.

Useful for decorate lambda functions with name and documentation.

As positional arguments could be passed the function to be decorated and the name in any order. So the next two identity definitions are equivalents:

>>> from xoutil.functools import lwraps as lw

>>> identity = lw('identity', lambda arg: arg)

>>> identity = lw(lambda arg: arg, 'identity')

As keyword arguments could be passed some special values, and any number of literal values to be assigned:

  • name: The name of the function (__name__); only valid if not given as positional argument.
  • doc: The documentation (__doc__ field).
  • wrapped: An object to extract all values not yet assigned. These values are (‘__module__’, ‘__name__’ and ‘__doc__’) to be assigned, and ‘__dict__’ to be updated.

If the function to decorate is present in the positional arguments, this same argument function is directly returned after decorated; if not a decorator is returned similar to standard wraps().

For example:

>>> from xoutil.validators.connote import lwraps as lw

>>> is_valid_age = lw('is-valid-human-age', lambda age: 0 < age <= 120,
...                   doc=('A predicate to evaluate if an age is '
...                        'valid for a human being.')

>>> @lw(wrapped=is_valid_age)
... def is_valid_working_age(age):
...     return 18 < age <= 70

>>> is_valid_age(16)
True

>>> is_valid_age(200)
False

>>> is_valid_working_age(16)
False

New in version 1.7.0.

xoutil.html – Helpers for manipulating HTML.

This module defines utilities to manipulate HTML.

This module backports several utilities from Python 3.2.

xoutil.html.escape(s, quote=True)[source]

Replace special characters “&”, “<” and “>” to HTML-safe sequences

If the optional flag quote is true (the default), the quotation mark characters, both double quote (”) and single quote (‘) characters are also translated.

Sub-modules on this package

xoutil.html.entities – Definitions of HTML general entities.

This module defines tree dictionaries, name2codepoint, codepoint2name, and entitydefs.

entitydefs is used to provide the entitydefs attribute of the xoutil.html.parser.HTMLParser class. The definition provided here contains all the entities defined by XHTML 1.0 that can be handled using simple textual substitution in the Latin-1 character set (ISO-8859-1).

xoutil.html.entities.entitydefs

A dictionary mapping XHTML 1.0 entity definitions to their replacement text in ISO Latin-1.

xoutil.html.entities.name2codepoint

A dictionary that maps HTML entity names to the Unicode codepoints.

xoutil.html.entities.codepoint2name

A dictionary that maps Unicode codepoints to HTML entity names

xoutil.html.parser – A simple parser that can handle HTML and XHTML.

This module defines a class HTMLParser which serves as the basis for parsing text files formatted in HTML (HyperText Mark-up Language) and XHTML.

Warning

This module has not being made Python 2.7 and 3.2 compatible.

class xoutil.html.parser.HTMLParser(strict=True)

Create a parser instance. If strict is True (the default), invalid HTML results in HTMLParseError exceptions [1]. If strict is False, the parser uses heuristics to make a best guess at the intention of any invalid HTML it encounters, similar to the way most browsers do. Using strict=False is advised.

An :class`HTMLParser` instance is fed HTML data and calls handler methods when start tags, end tags, text, comments, and other markup elements are encountered. The user should subclass HTMLParser and override its methods to implement the desired behavior.

This parser does not check that end tags match start tags or call the end-tag handler for elements which are closed implicitly by closing an outer element.

Changed in version 3.2: strict keyword added

class xoutil.html.parser.HTMLParseError

Exception raised by the HTMLParser class when it encounters an error while parsing and strict is True. This exception provides three attributes: msg is a brief message explaining the error, lineno is the number of the line on which the broken construct was detected, and offset is the number of characters into the line at which the construct starts.

xoutil.infinity - An infinite value

xoutil.infinity.Infinity

The positive infinite value. The negative infinite value is -Infinity.

These values are only sensible for comparison. Arithmetic is not supported.

The type of values that is comparable with Infinity is controlled by the ABC InfinityComparable.

class xoutil.infinity.InfinityComparable[source]

Any type that can be sensibly compared to infinity.

All types in the number tower are always comparable.

Classes datetime.date, datetime.datetime, and datetime.timedelta are automatically registered.

xoutil.inspect – Inspect live objects

Extensions to Python’s inspect module.

You may use it as drop-in replacement of inspect. Although we don’t document all items here. Refer to inspect's documentation.

class xoutil.inspect.FullArgSpec(args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, annotations)
annotations

Alias for field number 6

args

Alias for field number 0

defaults

Alias for field number 3

kwonlyargs

Alias for field number 4

kwonlydefaults

Alias for field number 5

varargs

Alias for field number 1

varkw

Alias for field number 2

xoutil.inspect.get_attr_value(obj, name, *default)[source]

Get a named attribute from an object in a safe way.

Similar to getattr but without triggering dynamic look-up via the descriptor protocol, __getattr__ or __getattribute__ by using getattr_static().

xoutil.inspect.getattr_static(obj, attr, default=<object object>)[source]

Retrieve attributes without triggering dynamic lookup via the descriptor protocol, __getattr__ or __getattribute__.

Note: this function may not be able to retrieve all attributes that getattr can fetch (like dynamically created attributes) and may find attributes that getattr can’t (like descriptors that raise AttributeError). It can also return descriptor objects instead of instance members in some cases. See the documentation for details.

xoutil.inspect.type_name(obj, affirm=False)[source]

Return the internal name for a type or a callable.

This function is safe. If :param obj: is not an instance of a proper type then returns the following depending on :param affirm:

  • If False returns None.
  • If True convert a single object to its type before returns the name, but if is a tuple, list or set; returns a string with a representation of contained types.

Examples:

>>> type_name(int)
'int'

>>> type_name(0) is None
True

>>> type_name(0, affirm=True)
'int'

>>> type_name((0, 1.1)) is None
True

>>> type_name((0, 1.1), affirm=True)
'(int, float)'

xoutil.iterators - Functions creating iterators for efficient looping

Several util functions for iterators

xoutil.iterators.dict_update_new(target, source)[source]

Update values in source that are new (not present) in target.

xoutil.iterators.first_n(iterable, n=1, fill=Unset)[source]

Takes the first n items from iterable.

If there are less than n items in the iterable and fill is Unset, a StopIteration exception is raised; otherwise it’s used as a filling pattern as explained below.

Parameters:
  • iterable – An iterable from which the first n items should be collected.
  • n (int) – The number of items to collect
  • fill

    The filling pattern to use. It may be:

    • a collection, in which case first_n fills the last items by cycling over fill.
    • anything else is used as the filling pattern by repeating.
Returns:

The first n items from iterable, probably with a filling pattern at the end.

Return type:

generator object

New in version 1.2.0.

Changed in version 1.4.0: The notion of collection for the fill argument uses xoutil.types.is_collection instead of probing for the __iter__ method.

xoutil.iterators.first_non_null(iterable, default=None)[source]

Returns the first value from iterable which is non-null.

This is roughly the same as:

next((x for x in iter(iterable) if x), default)

New in version 1.4.0.

xoutil.iterators.slides(iterable, width=2, fill=None)[source]

Creates a sliding window of a given width over an iterable:

>>> list(slides(range(1, 11)))
[(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]

If the iterator does not yield a width-aligned number of items, the last slice returned is filled with fill (by default None):

>>> list(slides(range(1, 11), width=3))   
[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, None, None)]

Changed in version 1.4.0: If the fill argument is a collection is cycled over to get the filling, just like in first_n().

Changed in version 1.4.2: The fill argument now defaults to None, instead of Unset.

xoutil.iterators.continuously_slides(iterable, width=2, fill=None)[source]

Similar to slides() but moves one item at the time (i.e continuously).

fill is only used to fill the fist chunk if the iterable has less items than the width of the window.

Example (generate a texts tri-grams):

>>> slider = continuously_slides(str('maupassant'), 3)
>>> list(str('').join(chunk) for chunk in slider)
['mau', 'aup', 'upa', 'pas', 'ass', 'ssa', 'san', 'ant']
xoutil.iterators.ungroup(iterator)[source]

Reverses the operation of itertools.groupby() (or similar).

The iterator should produce pairs of (_, xs); where xs is another iterator (or iterable).

It’s guaranteed that the iterator will be consumed at the boundaries of each pair, i.e. before taking another pair (_, ys) from iterator the first xs will be fully yielded.

Demonstration:

>>> def groups():
...    def chunk(s):
...       for x in range(s, s+3):
...           print('Yielding x:', x)
...           yield x
...
...    for g in range(2):
...       print('Yielding group', g)
...       yield g, chunk(g)
>>> list(ungroup(groups()))
Yielding group 0
Yielding x: 0
Yielding x: 1
Yielding x: 2
Yielding group 1
Yielding x: 1
Yielding x: 2
Yielding x: 3
[0, 1, 2, 1, 2, 3]

This is not the same as:

>>> import itertools
>>> xs = itertools.chain(*(xs for _, xs in groups()))
Yielding group 0
Yielding group 1

Notice that the iterator was fully consumed just to create the arguments to chain().

New in version 1.7.3.

xoutil.iterators.delete_duplicates(seq[, key=lambda x: x])[source]

Remove all duplicate elements from seq.

Two items x and y are considered equal (duplicates) if key(x) == key(y). By default key is the identity function.

Works with any sequence that supports len(), __getitem__() and addition.

Note

seq.__getitem__ should work properly with slices.

The return type will be the same as that of the original sequence.

New in version 1.5.5.

Changed in version 1.7.4: Added the key argument. Clarified the documentation: seq should also implement the __add__ method and that its __getitem__ method should deal with slices.

xoutil.iterators.iter_delete_duplicates(iter[, key=lambda x: x])[source]

Yields non-repeating items from iter.

key has the same meaning as in delete_duplicates().

Examples:

>>> list(iter_delete_duplicates('AAAaBBBA'))
['A', 'a', 'B', 'A']
>>> list(iter_delete_duplicates('AAAaBBBA', key=lambda x: x.lower()))
['A', 'B', 'A']

New in version 1.7.4.

xoutil.iterators.fake_dict_iteritems(source)[source]

Iterate (key, value) in a source that have defined method “keys” and __getitem__().

Warning

Deprecated since 1.7.0. This was actually in risk since 1.4.0.

xoutil.iterators.flatten(sequence, is_scalar=xoutil.types.is_scalar, depth=None)[source]

Flattens out a sequence. It takes care of everything deemed a collection (i.e, not a scalar according to the callabled passed in is_scalar):

>>> from xoutil.eight import range
>>> range_ = lambda *a: list(range(*a))
>>> tuple(flatten((1, range_(2, 5), range(5, 10))))
(1, 2, 3, 4, 5, 6, 7, 8, 9)

If depth is None the collection is flattened recursiverly until the “bottom” is reached. If depth is an integer then the collection is flattened up to that level. depth=0 means not to flatten. Nested iterators are not “exploded” if under the stated depth:

# In the following doctest we use ``...range(...X)`` because the string
# repr of range differs in Py2 and Py3k.

>>> tuple(flatten((range_(2), range(2, 4)), depth=0))  # doctest: +ELLIPSIS
([0, 1], ...range(2, 4))

>>> tuple(flatten((range(2), range_(2, 4)), depth=0))  # doctest: +ELLIPSIS
(...range(...2), [2, 3])
xoutil.iterators.zip([iter1[, iter2[, ...]]])

Return a zip-like object whose next() method returns a tuple where the i-th element comes from the i-th iterable argument. The next() method continues until the shortest iterable in the argument sequence is exhausted and then it raises StopIteration.

This method is actually the standard itertools.izip() when in Python 2.7, and the builtin zip when in Python 3.

xoutil.iterators.map(func, *iterables)

Make an iterator that computes the function using arguments from each of the iterables. It stops when the shortest iterable is exhausted instead of filling in None for shorter iterables.

This method is actually the stardard itertools.imap when in Python 2.7, and the builtin map when in Python 3.

xoutil.iterators.zip_longest(*iterables, fillvalue=None)

Make an iterator that aggregates elements from each of the iterables. If the iterables are of uneven length, missing values are filled-in with fillvalue. Iteration continues until the longest iterable is exhausted.

If one of the iterables is potentially infinite, then the zip_longest() function should be wrapped with something that limits the number of calls (for example islice() or takewhile()). If not specified, fillvalue defaults to None.

This function is actually an alias to itertools.izip_longest() in Python 2.7, and an alias to itertools.zip_longest() in Python 3.3.

xoutil.json - Encode and decode the JSON format

Extensions to the json standard library module.

It just adds the ability to encode/decode datetimes. But you should use the JSONEncoder yourself.

You may use this module as drop-in replacement to Python’s json. Also it contains definitions to use C library JSON speedups or Python replacements in case that library is not installed in your system.

xoutil.json.dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, encoding='utf-8', default=None, sort_keys=False, **kw)[source]

Serialize obj as a JSON formatted stream to fp (a .write()-supporting file-like object).

If skipkeys is true then dict keys that are not basic types (str, unicode, int, long, float, bool, None) will be skipped instead of raising a TypeError.

If ensure_ascii is true (the default), all non-ASCII characters in the output are escaped with \uXXXX sequences, and the result is a str instance consisting of ASCII characters only. If ensure_ascii is False, some chunks written to fp may be unicode instances. This usually happens because the input contains unicode strings or the encoding parameter is used. Unless fp.write() explicitly understands unicode (as in codecs.getwriter) this is likely to cause an error.

If check_circular is false, then the circular reference check for container types will be skipped and a circular reference will result in an OverflowError (or worse).

If allow_nan is false, then it will be a ValueError to serialize out of range float values (nan, inf, -inf) in strict compliance of the JSON specification, instead of using the JavaScript equivalents (NaN, Infinity, -Infinity).

If indent is a non-negative integer, then JSON array elements and object members will be pretty-printed with that indent level. An indent level of 0 will only insert newlines. None is the most compact representation. Since the default item separator is ', ', the output might include trailing whitespace when indent is specified. You can use separators=(',', ': ') to avoid this.

If separators is an (item_separator, dict_separator) tuple then it will be used instead of the default (', ', ': ') separators. (',', ':') is the most compact JSON representation.

encoding is the character encoding for str instances, default is UTF-8.

default(obj) is a function that should return a serializable version of obj or raise TypeError. The default simply raises TypeError.

If sort_keys is True (default: False), then the output of dictionaries will be sorted by key.

To use a custom JSONEncoder subclass (e.g. one that overrides the .default() method to serialize additional types), specify it with the cls kwarg; otherwise JSONEncoder is used.

xoutil.json.dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, encoding='utf-8', default=None, sort_keys=False, **kw)[source]

Serialize obj to a JSON formatted str.

If skipkeys is true then dict keys that are not basic types (str, unicode, int, long, float, bool, None) will be skipped instead of raising a TypeError.

If ensure_ascii is false, all non-ASCII characters are not escaped, and the return value may be a unicode instance. See dump for details.

If check_circular is false, then the circular reference check for container types will be skipped and a circular reference will result in an OverflowError (or worse).

If allow_nan is false, then it will be a ValueError to serialize out of range float values (nan, inf, -inf) in strict compliance of the JSON specification, instead of using the JavaScript equivalents (NaN, Infinity, -Infinity).

If indent is a non-negative integer, then JSON array elements and object members will be pretty-printed with that indent level. An indent level of 0 will only insert newlines. None is the most compact representation. Since the default item separator is ', ', the output might include trailing whitespace when indent is specified. You can use separators=(',', ': ') to avoid this.

If separators is an (item_separator, dict_separator) tuple then it will be used instead of the default (', ', ': ') separators. (',', ':') is the most compact JSON representation.

encoding is the character encoding for str instances, default is UTF-8.

default(obj) is a function that should return a serializable version of obj or raise TypeError. The default simply raises TypeError.

If sort_keys is True (default: False), then the output of dictionaries will be sorted by key.

To use a custom JSONEncoder subclass (e.g. one that overrides the .default() method to serialize additional types), specify it with the cls kwarg; otherwise JSONEncoder is used.

xoutil.json.load(fp, encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)[source]

Deserialize fp (a .read()-supporting file-like object containing a JSON document) to a Python object.

If the contents of fp is encoded with an ASCII based encoding other than utf-8 (e.g. latin-1), then an appropriate encoding name must be specified. Encodings that are not ASCII based (such as UCS-2) are not allowed, and should be wrapped with codecs.getreader(fp)(encoding), or simply decoded to a unicode object and passed to loads()

object_hook is an optional function that will be called with the result of any object literal decode (a dict). The return value of object_hook will be used instead of the dict. This feature can be used to implement custom decoders (e.g. JSON-RPC class hinting).

object_pairs_hook is an optional function that will be called with the result of any object literal decoded with an ordered list of pairs. The return value of object_pairs_hook will be used instead of the dict. This feature can be used to implement custom decoders that rely on the order that the key and value pairs are decoded (for example, collections.OrderedDict will remember the order of insertion). If object_hook is also defined, the object_pairs_hook takes priority.

To use a custom JSONDecoder subclass, specify it with the cls kwarg; otherwise JSONDecoder is used.

xoutil.json.loads(s, encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)[source]

Deserialize s (a str or unicode instance containing a JSON document) to a Python object.

If s is a str instance and is encoded with an ASCII based encoding other than utf-8 (e.g. latin-1) then an appropriate encoding name must be specified. Encodings that are not ASCII based (such as UCS-2) are not allowed and should be decoded to unicode first.

object_hook is an optional function that will be called with the result of any object literal decode (a dict). The return value of object_hook will be used instead of the dict. This feature can be used to implement custom decoders (e.g. JSON-RPC class hinting).

object_pairs_hook is an optional function that will be called with the result of any object literal decoded with an ordered list of pairs. The return value of object_pairs_hook will be used instead of the dict. This feature can be used to implement custom decoders that rely on the order that the key and value pairs are decoded (for example, collections.OrderedDict will remember the order of insertion). If object_hook is also defined, the object_pairs_hook takes priority.

parse_float, if specified, will be called with the string of every JSON float to be decoded. By default this is equivalent to float(num_str). This can be used to use another datatype or parser for JSON floats (e.g. decimal.Decimal).

parse_int, if specified, will be called with the string of every JSON int to be decoded. By default this is equivalent to int(num_str). This can be used to use another datatype or parser for JSON integers (e.g. float).

parse_constant, if specified, will be called with one of the following strings: -Infinity, Infinity, NaN, null, true, false. This can be used to raise an exception if invalid JSON numbers are encountered.

To use a custom JSONDecoder subclass, specify it with the cls kwarg; otherwise JSONDecoder is used.

class xoutil.json.JSONDecoder(encoding=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, strict=True, object_pairs_hook=None)[source]

Simple JSON <http://json.org> decoder

Performs the following translations in decoding by default:

JSON Python
object dict
array list
string unicode
number (int) int, long
number (real) float
true True
false False
null None

It also understands NaN, Infinity, and -Infinity as their corresponding float values, which is outside the JSON spec.

decode(s, _w=<built-in method match of _sre.SRE_Pattern object>)[source]

Return the Python representation of s (a str or unicode instance containing a JSON document)

raw_decode(s, idx=0)[source]

Decode a JSON document from s (a str or unicode beginning with a JSON document) and return a 2-tuple of the Python representation and the index in s where the document ended.

This can be used to decode a JSON document from a string that may have extraneous data at the end.

class xoutil.json.JSONEncoder(skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False, indent=None, separators=None, encoding='utf-8', default=None)[source]

Extensible JSON <http://json.org> encoder for Python data structures.

Supports the following objects and types by default:

Python JSON
dict object
list, tuple array
str, unicode string
int, long, float number
True true
False false
None null

To extend this to recognize other objects, subclass and implement a .default() method with another method that returns a serializable object for o if possible, otherwise it should call the superclass implementation (to raise TypeError).

We also support:

  • datetime, date and time values, which are translated to strings using ISO format.
  • Decimal values, which are represented as a string representation.
  • Iterables, which are represented as lists.

xoutil.logger - Standard logger helpers

Usage:

logger.debug('Some debug message')

Basically you may request any of the loggers attribute/method and this module will return the logger’s attribute corresponding to the loggers of the calling module. This avoids the boilerplate seen in most codes:

logger = logging.getLogger(__name__)

You may simply do:

from xoutil.logger import debug
debug('Some debug message')

The proper logger will be selected by this module.

Note

Notice this won’t configure any handler for you. Only the calling pattern is affected. You must configure your loggers as usual.

xoutil.modules – Utilities for working with modules

Modules utilities.

xoutil.modules.copy_members(source=None, target=None)[source]

Copy module members from source to target.

It’s common in xoutil package to extend Python modules with the same name, for example xoutil.datetime has all public members of Python’s datetime. copy_members() can be used to copy all members from the original module to the extended one.

Parameters:
  • source

    string with source module name or module itself.

    If not given, is assumed as the last module part name of target.

  • target

    string with target module name or module itself.

    If not given, target name is looked in the stack of caller module.

Returns:

Source module.

Return type:

ModuleType

Warning

Implementation detail

Function used to inspect the stack is not guaranteed to exist in all implementations of Python.

xoutil.modules.customize(module, custom_attrs=None, meta=None)[source]

Replaces a module by a custom one.

Injects all kwargs into the newly created module’s class. This allows to have module into which we may have properties or other type of descriptors.

Parameters:
  • module – The module object to customize.
  • custom_attrs

    A dictionary of custom attributes that should be injected in the customized module.

    New in version 1.4.2: Changes the API, no longer uses the **kwargs idiom for custom attributes.

  • meta – The metaclass of the module type. This should be a subclass of type. We will actually subclass this metaclass to properly inject custom_attrs in our own internal metaclass.
Returns:

A tuple of (module, customized, class) with the module in the first place, customized will be True only if the module was created (i.e customize() is idempotent), and the third item will be the class of the module (the first item).

xoutil.modules.force_module(ref=None)[source]

Load a module from a string or return module if already created.

If ref is not specified (or integer) calling module is assumed looking in the stack.

Note

Implementation detail

Function used to inspect the stack is not guaranteed to exist in all implementations of Python.

xoutil.modules.get_module_path(module)[source]

Gets the absolute path of a module.

Parameters:module – Either module object or a (dotted) string for the module.
Returns:The path of the module.

If the module is a package, returns the directory path (not the path to the __init__).

If module is a string and it’s not absolute, raises a TypeError.

xoutil.modules.modulemethod(func)[source]

Decorator that defines a module-level method.

Simply a module-level method, will always receive a first argument self with the module object.

xoutil.modules.moduleproperty(getter, setter=None, deleter=None, doc=None, base=<type 'property'>)[source]

Decorator that creates a module-level property.

The module of the getter is replaced by a custom implementation of the module, and the property is injected to the custom module’s class.

The parameter base serves the purpose of changing the base for the property. For instance, this allows you to have memoized_properties at the module-level:

def memoized(self):
    return self
memoized = moduleproperty(memoized, base=memoized_property)

xoutil.names – Utilities for handling objects names

A protocol to obtain or manage object names.

xoutil.names.nameof(*objects, depth=1, inner=False, typed=False, full=False, safe=False)[source]

Obtain the name of each one of a set of objects.

New in version 1.4.0.

    Changed in version 1.6.0:
  • Keyword arguments are now keyword-only arguments.

  • Support for several objects

  • Improved the semantics of parameter full.

  • Added the safe keyword argument.

If no object is given, None is returned; if only one object is given, a single string is returned; otherwise a list of strings is returned.

The name of an object is normally the variable name in the calling stack.

If the object is not present calling frame, up to five frame levels are searched. Use the depth keyword argument to specify a different starting point and the search will proceed five levels from this frame up.

If the same object has several good names a single one is arbitrarily chosen.

Good names candidates are retrieved based on the keywords arguments full, inner, safe and typed.

If typed is True and the object is not a type name or a callable (see xoutil.inspect.type_name()), then the type of the object is used instead.

If inner is True we try to extract the name by introspection instead of looking for the object in the frame stack:

If full is True the full identifier of the object is preferred. In this case if inner is False the local-name for the object is found. If inner is True, find the import-name.

If safe is True, returned value is converted -if it is not- into a valid Python identifier, though you should not trust this identifier resolves to the value.

See the examples in the documentation.

xoutil.names.identifier_from(obj)[source]

Build an valid identifier from the name extracted from an object.

New in version 1.5.6.

First, check if argument is a type and then returns the name of the type prefixed with _ if valid; otherwise calls nameof function repeatedly until a valid identifier is found using the following order logic: inner=True, without arguments looking-up a variable in the calling stack, and typed=True. Returns None if no valid value is found.

Examples:

>>> identifier_from({})
'dict'

Use cases for getting the name of an object

The function nameof() is useful for cases when you get a value and you need a name. This is a common need when doing framework-level code that tries to avoid repetition of concepts.

Solutions with nameof()
Properly calculate the tasks’ name in Celery applications

Celery warns about how to import the tasks. If in a module you import your task using an absolute import, and in another module you import it using a relative import, Celery regards them as different tasks. You must either use a consistent import style, or give a name for the task. Using nameof you can easily fix this problem.

Assume you create a celapp.tasks.basic module with this code:

>>> def celery_task(celeryapp, *args, **kwargs):
...    def decorator(func):
...        from xoutil.names import nameof
...        taskname = nameof(func, full=True, inner=True)
...        return celeryapp.task(name=taskname, *args, **kwargs)(func)
...    return decorator

>>> from celery import Celery
>>> app = Celery()
>>> @celery_task(app)
... def add(x, y):
...     return x + y

Then importing the task directly in a shell will have the correct name:

>>> from celapp.tasks.basic import add
>>> add.name
'celapp.tasks.basic.add'

Another module that imports the task will also see the proper name. Say you have the module celapp.consumer:

>>> from .tasks import basic

>>> def get_name(taskname):
...     task = getattr(basic, taskname)
...     return task.name

Then:

>>> from celapp.consumer import get_name
>>> get_name('add')
'celapp.tasks.basic.add'

Despite that you imported the basic module with a relative import the name is fully calculated.

xoutil.objects - Functions for dealing with objects

Several utilities for objects in general.

xoutil.objects.validate_attrs(source, target, force_equals=(), force_differents=())[source]

Makes a ‘comparison’ of source and target by its attributes (or keys).

This function returns True if and only if both of these tests pass:

  • All attributes in force_equals are equal in source and target
  • All attributes in force_differents are different in source and target

For instance:

>>> class Person(object):
...    def __init__(self, **kwargs):
...        for which in kwargs:
...            setattr(self, which, kwargs[which])

>>> source = Person(name='Manuel', age=33, sex='male')
>>> target = {'name': 'Manuel', 'age': 4, 'sex': 'male'}

>>> validate_attrs(source, target, force_equals=('sex',),
...                force_differents=('age',))
True

>>> validate_attrs(source, target, force_equals=('age',))
False

If both force_equals and force_differents are empty it will return True:

>>> validate_attrs(source, target)
True
xoutil.objects.iterate_over(source, *keys)[source]

Yields pairs of (key, value) for of all keys in source.

If any key is missing from source is ignored (not yielded).

If source is a collection, iterate over each of the items searching for any of keys. This is not recursive.

If no keys are provided, return an “empty” iterator – i.e will raise StopIteration upon calling next.

New in version 1.5.2.

xoutil.objects.smart_getter(obj, strict=False)[source]

Returns a smart getter for obj.

If obj is a mapping, it returns the .get() method bound to the object obj, otherwise it returns a partial of getattr on obj.

Parameters:strict – Set this to True so that the returned getter checks that keys/attrs exists. If strict is True the getter may raise a KeyError or an AttributeError.

Changed in version 1.5.3: Added the parameter strict.

xoutil.objects.smart_getter_and_deleter(obj)[source]

Returns a function that get and deletes either a key or an attribute of obj depending on the type of obj.

If obj is a collections.Mapping it must be a collections.MutableMapping.

xoutil.objects.popattr(obj, name, default=None)[source]

Looks for an attribute in the obj and returns its value and removes the attribute. If the attribute is not found, default is returned instead.

Examples:

>>> class Foo(object):
...   a = 1
>>> foo = Foo()
>>> foo.a = 2
>>> popattr(foo, 'a')
2
>>> popattr(foo, 'a')
1
>>> popattr(foo, 'a') is None
True
xoutil.objects.setdefaultattr(obj, name, value)[source]

Sets the attribute name to value if it is not set:

>>> class Someclass(object): pass
>>> inst = Someclass()
>>> setdefaultattr(inst, 'foo', 'bar')
'bar'

>>> inst.foo
'bar'

>>> inst.spam = 'egg'
>>> setdefaultattr(inst, 'spam', 'with ham')
'egg'

(New in version 1.2.1). If you want the value to be lazily evaluated you may provide a lazy-lambda:

>>> inst = Someclass()
>>> inst.a = 1
>>> def setting_a():
...    print('Evaluating!')
...    return 'a'

>>> setdefaultattr(inst, 'a', lazy(setting_a))
1

>>> setdefaultattr(inst, 'ab', lazy(setting_a))
Evaluating!
'a'
xoutil.objects.copy_class(cls, meta=None, ignores=None, new_attrs=None, new_name=None)[source]

Copies a class definition to a new class.

The returned class will have the same name, bases and module of cls.

Parameters:
  • meta – If None, the type(cls) of the class is used to build the new class, otherwise this must be a proper metaclass.
  • ignores

    A sequence of attributes names that should not be copied to the new class.

    An item may be callable accepting a single argument attr that must return a non-null value if the the attr should be ignored.

  • new_attrs (dict) – New attributes the class must have. These will take precedence over the attributes in the original class.
  • new_name – The name for the copy. If not provided the name will copied.

New in version 1.4.0.

Changed in version 1.7.1: The ignores argument must an iterable of strings or callables. Removed the glob-pattern and regular expressions as possible values. They are all possible via the callable variant.

New in version 1.7.1: The new_name argument.

xoutil.objects.fulldir(obj)[source]

Return a set with all attribute names defined in obj

class xoutil.objects.classproperty(fget)[source]

A descriptor that behaves like property for instances but for classes.

Example of its use:

class Foobar(object):
    @classproperty
    def getx(cls):
        return cls._x

Class properties are always read-only, if attribute values must be set or deleted, a metaclass must be defined.

xoutil.objects.get_first_of(sources, *keys, default=None, pred=None)[source]

Return the value of the first occurrence of any of the specified keys in source that matches pred (if given).

Both source and keys has the same meaning as in iterate_over().

Parameters:
  • default – A value to be returned if no key is found in source.
  • pred – A function that should receive a single value and return False if the value is not acceptable, and thus get_first_of should look for another.

Changed in version 1.5.2: Added the pred option.

xoutil.objects.xdir(obj, filter=None, attr_filter=None, value_filter=None, getattr=None)[source]

Return all (attr, value) pairs from obj that attr_filter(attr) and value_filter(value) are both True.

Parameters:
  • obj – The object to be instrospected.
  • filter

    optional A filter that will be passed both the attribute name and it’s value as two positional arguments. It should return True for attrs that should be yielded.

    Note

    If passed, both attr_filter and value_filter will be ignored.

  • attr_filteroptional A filter for attribute names. Deprecated since 1.4.1
  • value_filteroptional A filter for attribute values. Deprecated since 1.4.1
  • getteroptional A function with the same signature that getattr to be used to get the values from obj.

Deprecated since version 1.4.1: The use of params attr_filter and value_filter.

xoutil.objects.fdir(obj, filter=None, attr_filter=None, value_filter=None, getattr=None)[source]

Similar to xdir() but yields only the attributes names.

xoutil.objects.smart_copy(*sources, target, *, defaults=False)[source]

Copies the first apparition of attributes (or keys) from sources to target.

Parameters:
  • sources – The objects from which to extract keys or attributes.
  • target – The object to fill.
  • defaults (Either a bool, a dictionary, an iterable or a callable.) – Default values for the attributes to be copied as explained below. Defaults to False.

Every sources and target are always positional arguments. There should be at least one source. target will always be the last positional argument.

If defaults is a dictionary or an iterable then only the names provided by itering over defaults will be copied. If defaults is a dictionary, and one of its key is not found in any of the sources, then the value of the key in the dictionary is copied to target unless:

In these cases a KeyError is raised if the key is not found in the sources.

If default is an iterable and a key is not found in any of the sources, None is copied to target.

If defaults is a callable then it should receive one positional arguments for the current attribute name and several keyword arguments (we pass source) and return either True or False if the attribute should be copied.

If defaults is False (or None) only the attributes that do not start with a “_” are copied, if it’s True all attributes are copied.

When target is not a mapping only valid Python identifiers will be copied.

Each source is considered a mapping if it’s an instance of collections.Mapping or a MappingProxyType.

The target is considered a mapping if it’s an instance of collections.MutableMapping.

Returns:target.

Changed in version 1.7.0: defaults is now keyword only.

xoutil.objects.extract_attrs(obj, *names, default=Unset)[source]

Extracts all names from an object.

If obj is a Mapping, the names will be search in the keys of the obj; otherwise the names are considered regular attribute names.

If default is Unset and any name is not found, an AttributeError is raised, otherwise the default is used instead.

Returns a tuple if there are more that one name, otherwise returns a single value.

New in version 1.4.0.

Changed in version 1.5.3: Each name may be a path like in get_traverser(), but only ”.” is allowed as separator.

xoutil.objects.traverse(obj, path, default=Unset, sep='.', getter=None)[source]

Traverses an object’s hierarchy by performing an attribute get at each level.

This helps getting an attribute that is buried down several levels deep. For example:

traverse(request, 'session.somevalue')

If default is not provided (i.e is Unset) and any component in the path is not found an AttributeError exceptions is raised.

You may provide sep to change the default separator.

You may provide a custom getter. By default, does an smart_getter() over the objects. If provided getter should have the signature of getattr().

See get_traverser() if you need to apply the same path(s) to several objects. Actually this is equivalent to:

get_traverser(path, default=default, sep=sep, getter=getter)(obj)
xoutil.objects.get_traverser(*paths, default=Unset, sep='.', getter=None)[source]

Combines the power of traverse() with the expectations from both operator.itergetter() and operator.attrgetter().

Parameters:paths – Several paths to extract.

Keyword arguments has the same meaning as in traverse().

Returns:A function the when invoked with an object traverse the object finding each path.

New in version 1.5.3.

xoutil.objects.dict_merge(*dicts, **other)[source]

Merges several dicts into a single one.

Merging is similar to updating a dict, but if values are non-scalars they are also merged is this way:

  • Any two sequences or sets are joined together.
  • Any two mappings are recursively merged.
  • Other types are just replaced like in update().

If for a single key two values of incompatible types are found, raise a TypeError. If the values for a single key are compatible but different (i.e a list an a tuple) the resultant type will be the type of the first apparition of the key, unless for mappings which are always cast to dicts.

No matter the types of dicts the result is always a dict.

Without arguments, return the empty dict.

xoutil.objects.smart_getattr(name, *sources, **kwargs)[source]

Gets an attr by name for the first source that has it.

This is roughly that same as:

get_first_of(sources, name, default=Unset, **kwargs)

Warning

Deprecated since 1.5.1

xoutil.objects.pop_first_of(source, *keys, default=None)[source]

Similar to get_first_of() using as source either an object or a mapping and deleting the first attribute or key.

Examples:

>>> somedict = dict(bar='bar-dict', eggs='eggs-dict')

>>> class Foo(object): pass
>>> foo = Foo()
>>> foo.bar = 'bar-obj'
>>> foo.eggs = 'eggs-obj'

>>> pop_first_of((somedict, foo), 'eggs')
'eggs-dict'

>>> pop_first_of((somedict, foo), 'eggs')
'eggs-obj'

>>> pop_first_of((somedict, foo), 'eggs') is None
True

>>> pop_first_of((foo, somedict), 'bar')
'bar-obj'

>>> pop_first_of((foo, somedict), 'bar')
'bar-dict'

>>> pop_first_of((foo, somedict), 'bar') is None
True
xoutil.objects.get_and_del_attr(obj, name, default=None)

Deprecated alias for popattr().

xoutil.objects.get_and_del_first_of(source, *keys, default=None)

Deprecated alias for pop_first_of().

class xoutil.objects.metaclass(meta, **kwargs)[source]

Deprecated alias of xoutil.eight.meta.metaclass.

New in version 1.4.1.

Changed in version 1.7.0: Deprecated in favor of xoutil.eight.meta.metaclass().

xoutil.objects.fix_method_documentation(cls, method_name, ignore=None, min_length=10, deep=1, default=None)[source]

Fix the documentation for the given class using its super-classes.

This function may be useful for shells or Python Command Line Interfaces (CLI).

If cls has an invalid documentation, super-classes are recursed in MRO until a documentation definition was made at any level.

Parameters:
  • ignore – could be used to specify which classes to ignore by specifying its name in this list.
  • min_length – specify that documentations with less that a number of characters, also are ignored.
xoutil.objects.multi_getter(source, *ids)[source]

Get values from source of all given ids.

Parameters:
  • source – Any object but dealing with differences between mappings and other object types.
  • ids

    Identifiers to get values from source.

    An ID item could be:

    • a string: is considered a key, if source is a mapping, or an attribute name if source is an instance of any other type.
    • a collection of strings: find the first valid value in source evaluating each item in this collection using the above logic.

Example:

>>> d = {'x': 1, 'y': 2, 'z': 3}
>>> list(multi_getter(d, 'a', ('y', 'x'), ('x', 'y'), ('a', 'z', 'x')))
[None, 2, 1, 3]

>>> next(multi_getter(d, ('y', 'x'), ('x', 'y')), '---')
2

>>> next(multi_getter(d, 'a', ('b', 'c'), ('e', 'f')), '---') is None
True

New in version 1.7.1.

xoutil.objects.get_branch_subclasses(cls)[source]

Similar to type.__subclasses__() but recursive.

Only return sub-classes in branches (those with no sub-classes). Instead of returning a list, yield each valid value.

New in version 1.7.0.

xoutil.params – Function signatures

Conformer for function parameter passing.

It’s usual to declare functions with generic prototypes:

def func(*args, **kwargs):
    ...

Actual parameters must be identified in a smart way. This module provide a tool to solve argument identification from a definition in a dictionary:

{
  'main-name': (checker, pos-definition, aliases, default-value),
  ...
}
  • checker: A function that must validate a value; if valid return the same or a coerced value; if invalid must return the special value Invalid. If not given, identity function is used (check as valid all values, avoid this).

  • pos-definition: Define if the parameter could appear as a positional argument or not. Must be a set of positive integers defining priority orders, parameters with minor values must appear first. More than value means several alternatives.

    If not given, means that the parameter could not appear in positional arguments.

  • aliases: A set of strings (valid Python identifiers), alternatives that could be used as keyword names.

  • default: The default value to use if the argument is not given. The special value Undefined is used to specify that the parameter is required.

The value with each definition could miss several elements, each concept is identified by its type, but ambiguities must be avoided; if default value is confusing with some concept, must be the last one.

For example:

scheme = {
    'stream': (check_file_like, {0, 3}, {'output'}, sys.stdout),
    'indent': (check_positive_int, {1}, 1),
    'width': (check_positive_int, {2}, {'max_width'}, 79),
    'newline': (check_str, '\n'),
}

New in version 1.7.0.

class xoutil.params.ParamConformer(*schemes, **kwargs)[source]

Standardize actual parameters using a scheme.

xoutil.pprint – Extension to the data pretty printer.

This modules includes all the Python’s standard library features in module pprint and adds the function ppformat(), which just returns a string of the pretty-formatted object.

New in version 1.4.1.

xoutil.pprint.ppformat(obj)[source]

Just like pprint() but always returns the result instead of writing it to a stream.

Returns:The pretty formated text.
Return type:unicode in Python 2, str in Python 3.

xoutil.progress - Console progress utils

Tool to show a progress percent in the terminal.

class xoutil.progress.Progress(max_value=100, delta=1, first_message=None, display_width=None)[source]

Print a progress percent to the console. Also the elapsed and the estimated times.

To signal an increment in progress just call the instance and (optionally) pass a message like in:

progress = Progress(10)
for i in range(10):
    progress()

xoutil.records - Records definitions.

Records definitions.

A record allows to describe plain external data and a simplified model to read it. The main use of records is to represent data that is read from a CSV file.

See the record class to find out how to use it.

class xoutil.records.record(raw_data)[source]

Base record class.

Records allow to represent a sequence or mapping of values extracted from external sources into a dict-like Python value.

The first use-case for this abstraction is importing data from a CSV file. You could represent each line as an instance of a properly defined record.

An instance of a record would represent a single line (or row) from the external data source.

Records are expected to declare fields. Each field must be a CAPITALIZED valid identifier like:

>>> class INVOICE(record):
...     ID = 0
...     REFERENCE = 1

Fields must be integers or plain strings. Fields must not begin with an underscore (“_”). External data lines are required to support indexes of those types.

You could use either the classmethod get_field() to get the value of field in a single line (data as provided by the external source):

>>> line = (1, 'AA20X138874Z012')
>>> INVOICE.get_field(line, INVOICE.REFERENCE)
'AA20X138874Z012'

You may also have an instance:

>>> invoice = INVOICE(line)
>>> invoice.reference
'AA20X138874Z012'

Note

Instances attributes are renamed to lowercase. So you must not create any other attribute that has the same name as a field in lowercase, or else it will be overwritten.

You could define readers for any field. For instance if you have a “CREATED_DATETIME” field you may create a “_created_datetime_reader” function that will be used to parse the raw value of the instance into an expected type. See the included readers builders below.

Readers are always cast as staticmethods, whether or not you have explicitly stated that fact:

>>> from dateutil import parser
>>> class BETTER_INVOICE(INVOICE):
...     CREATED_TIME = 2
...     _created_time_reader = lambda val: parser.parse(val)
...

>>> line = (1, 'AA20X138874Z012', '2014-02-17T17:29:21.965053')
>>> BETTER_INVOICE.get_field(line, BETTER_INVOICE.CREATED_TIME)
datetime.datetime(2014, 2, 17, 17, 29, 21, 965053)

Warning

Creating readers for fields defined in super classes is not directly supported. To do so, you must declare the reader as a staticmethod yourself.

Note

Currently there’s no concept of relationship between rows in this model. We are evaluating whether by placing a some sort of context into the kwargs argument would be possible to write readers that fetch other instances.

Included reader builders

The following functions build readers for standards types.

Note

You cannot use these functions themselves as readers, but you must call them to obtain the desired reader.

All these functions have a pair of keywords arguments nullable and default. The argument nullable indicates whether the value must be present or not. The function check_nullable() implements this check and allows other to create their own builders with the same semantic.

xoutil.records.datetime_reader(format, nullable=False, default=None, strict=True)[source]

Returns a datetime reader.

Parameters:
  • format – The format the datetime is expected to be in the external data. This is passed to datetime.datetime.strptime().
  • strict – Whether to be strict about datetime format.

The reader works first by passing the value to strict datetime.datetime.strptime() function. If that fails with a ValueError and strict is True the reader fails entirely.

If strict is False, the worker applies different rules. First if the dateutil package is installed its parser module is tried. If dateutil is not available and nullable is True, return None; if nullable is False and default is not null (as in isnull()), return default, otherwise raise a ValueError.

xoutil.records.boolean_reader(true=('1', ), nullable=False, default=None)[source]

Returns a boolean reader.

Parameters:true – A collection of raw values considered to be True. Only the values in this collection will be considered True values.
xoutil.records.integer_reader(nullable=False, default=None)[source]

Returns an integer reader.

xoutil.records.decimal_reader(nullable=False, default=None)[source]

Returns a Decimal reader.

xoutil.records.float_reader(nullable=False, default=None)[source]

Returns a float reader.

xoutil.records.date_reader(format, nullable=False, default=None, strict=True)[source]

Return a date reader.

This is similar to datetime_reader() but instead of returning a datetime.datetime it returns a datetime.date.

Actually this function delegates to datetime_reader() most of its functionality.

Checking for null values
xoutil.records.isnull(val)[source]

Return True if val is null.

Null values are None, the empty string and any False instance of xoutil.logical.Logical.

Notice that 0, the empty list and other false values in Python are not considered null. This allows that the CSV null (the empty string) is correctly treated while other sources that provide numbers (and 0 is a valid number) are not misinterpreted as null.

xoutil.records.check_nullable(val, nullable)[source]

Check the restriction of nullable.

Return True if the val is non-null. If nullable is True and the val is null returns False. If nullable is False and val is null, raise a ValueError.

Test for null is done with function isnull().

These couple of functions allows you to define new builders that use the same null concept. For instance, if you need readers that parse dates in diferent locales you may do:

def date_reader(nullable=False, default=None, locale=None):
    from xoutil.records import check_nullable
    from babel.dates import parse_date, LC_TIME
    from datetime import datetime
    if not locale:
        locale = LC_TIME

    def reader(value):
        if check_nullable(value, nullable):
            return parse_date(value, locale=locale)
        else:
            return default
    return reader

xoutil.string - Common string operations.

Exposes all original string module functionalities, with some general additions.

In this module str and unicode types are not used because Python 2.x and Python 3.x treats strings differently. bytes and text_type will be used instead with the following conventions:

  • In Python 2.x str is synonym of bytes and both (unicode and ‘str’) are both string types inheriting form basestring.

  • In Python 3.x str is always unicode but unicode and basestring types doesn’t exists. bytes type can be used as an array of one byte each item.

    Many methods are readjusted to these conditions.

xoutil.string.capitalize(value, title=True)[source]

Capitalizes value according to whether it should be title-like.

Title-like means it will capitalize every word but the 3-letters or less unless its the first word:

>>> capitalize('a group is its own worst enemy')
'A Group is its own Worst Enemy'

(This may be odd because, in the example above, own should be capitalized.)

Return bytes or unicode depending on type of value.

>>> from xoutil.eight import text_type
>>> type(capitalize(text_type('something'))) is text_type
True
>>> type(capitalize(str('something'))) is str
True
xoutil.string.capitalize_word(value)[source]

Capitalizes the first char of value

xoutil.string.cut_any_prefix(value, *prefixes)[source]

Apply cut_prefix() for the first matching prefix.

xoutil.string.cut_any_suffix(value, *suffixes)[source]

Apply cut_suffix() for the first matching suffix.

xoutil.string.cut_prefix(value, prefix)[source]

Removes the leading prefix if exists, else return value unchanged.

xoutil.string.cut_prefixes(value, *prefixes)[source]

Apply cut_prefix() for all provided prefixes in order.

xoutil.string.cut_suffix(value, suffix)[source]

Removes the tailing suffix if exists, else return value unchanged.

xoutil.string.cut_suffixes(value, *suffixes)[source]

Apply cut_suffix() for all provided suffixes in order.

xoutil.string.error2str(error)[source]

Convert an error to string.

xoutil.string.force_encoding(encoding=None)[source]

Validates an encoding value; if None use locale.getlocale()[1]; else return the same value.

New in version 1.2.0.

xoutil.string.force_str(value, encoding=None)[source]

Force to string, the type is different in Python 2 or 3 (bytes or unicode).

Parameters:
  • value – The value to convert to str.
  • encoding

    The encoding which should be used if either encoding or decoding should be performed on value.

    The default is to use the same default as safe_encode() or safe_decode().

New in version 1.2.0.

xoutil.string.hyphen_name(name)[source]

Convert a name, normally an identifier, to a hyphened slug.

All transitions from lower to upper capitals (or from digits to letters) are joined with a hyphen.

Also, all invalid characters (those invalid in Python identifiers) are converted to hyphens.

For example:

>>> hyphen_name('BaseNode') == 'base-node'
True
xoutil.string.make_a10z(string)[source]

Utility to find out that “internationalization” is “i18n”.

Examples:

>>> print(make_a10z('parametrization'))
p13n
xoutil.string.normalize_ascii(value)[source]

Return the string normal form for the value

Convert all non-ascii to valid characters using unicode ‘NFKC’ normalization.

xoutil.string.normalize_name(value)[source]
xoutil.string.normalize_slug(value, replacement='-', invalids=None, valids=None)[source]

Return the string normal form, valid for slugs, for the value

Convert all non-ascii to valid characters using unicode ‘NFKC’ normalization.

Lower-case the result.

Replace unwanted characters by replacement, repetition of given pattern will be converted to only one instance.

Warning

There’s a known bug when replacement contains ‘’.

[_a-z0-9] are assumed as valid characters. Extra arguments can modify this standard behaviour:

Parameters:
  • invalids – Any collection of characters added to these that are normally invalid in the provided value. (non-ascii or not included in valid characters). Boolean True can be passed as a synonymous of "_" for compatibility with old invalid_underscore argument. False or None are assumed as an empty set for invalid characters.
  • valids – A collection of extra valid characters (all non-ascii characters are ignored). This parameter could be either a valid string, any iterator of valid strings of characters, or None to use only default valid characters (See above).

Warning

The result may contain characters in invalids if replacements does.

Parameters value and replacement could be of any (non-string) type, these values are normalized and converted to lower-case ASCII strings.

Examples:

>>> normalize_slug('  Á.e i  Ó  u  ') == 'a-e-i-o-u'
True

>>> normalize_slug('  Á.e i  Ó  u  ', '.', invalids='AU') == 'e.i.o'
True

>>> normalize_slug('  Á.e i  Ó  u  ', valids='.') == 'a.e-i-o-u'
True

>>> normalize_slug('_x', '_') == '_x'
True

>>> normalize_slug('-x', '_') == 'x'
True

>>> normalize_slug(None) == 'none'
True

>>> normalize_slug(1 == 1)  == 'true'
True

>>> normalize_slug(1.0) == '1-0'
True

>>> normalize_slug(135) == '135'
True

>>> normalize_slug(123456, '', invalids='52') == '1346'
True

>>> normalize_slug('_x', '_') == '_x'
True

Changed in version 1.5.5: Added the invalid_underscore parameter.

Changed in version 1.6.6: Replaced the invalid_underscore paremeter by invalids. Added the valids parameter.

Changed in version 1.7.2: Clarified the role of invalids with regards to replacement.

xoutil.string.normalize_str(value)[source]
xoutil.string.normalize_title(value)[source]
xoutil.string.normalize_unicode(value)[source]
xoutil.string.parse_boolean(value)[source]

Parse a boolean from any value given a special treatment to strings.

>>> parse_boolean('trUe')
True
>>> parse_boolean('faLSe')
False
xoutil.string.parse_url_int(value, default=None)[source]

Parse an integer URL argument. Some operations treat simple arguments as a list of one element.

xoutil.string.safe_decode(s, encoding=None)[source]

Similar to bytes decode method returning unicode.

Decodes s using the given encoding, or determining one from the system.

Returning type depend on python version; if 2.x is unicode if 3.x str.

New in version 1.1.3.

xoutil.string.safe_encode(u, encoding=None)[source]

Similar to unicode encode method returning bytes.

Encodes u using the given encoding, or determining one from the system.

Returning type is always bytes; but in python 2.x is also str.

New in version 1.1.3.

xoutil.string.safe_join(separator, iterable, encoding=None)[source]

Similar to join method in string objects separator.join(iterable), a string which is the concatenation of the strings in the iterable with separator as intermediate between elements. Return unicode or bytes depending on type of separator and each item in iterable.

encoding is used in case of error to concatenate bytes + unicode.

This function must be deprecated in Python 3.

New in version 1.1.3.

Warning

The force_separator_type was removed in version 1.2.0.

xoutil.string.safe_str(obj='')[source]

Convert to normal string type in a safe way.

Most of our Python 2.x code uses unicode as normal string, also in Python 3 converting bytes or byte-arrays to strings includes the “b” prefix in the resulting value.

This function is useful in some scenarios that require str type (for example attribute __name__ in functions and types).

As str is bytes in Python2, using str(value) assures correct these scenarios in most cases, but in other is not enough, for example:

>>> from xoutil.string import safe_str as sstr
>>> def inverted_partial(func, *args, **keywords):
...     def inner(*a, **kw):
...         a += args
...         kw.update(keywords)
...         return func(*a, **kw)
...     inner.__name__ = sstr(func.__name__.replace('lambda', u'λ'))
...     return inner

New in version 1.7.0.

xoutil.string.safe_strip(value)[source]

Removes the leading and tailing space-chars from value if string, else return value unchanged.

New in version 1.1.3.

xoutil.string.strfnumber(number, format_spec='%0.2f')[source]

xoutil.subprocess - Extensions to subprocess stardard module

New in version 1.2.1.

This module contains extensions to the subprocess standard library module. It may be used as a replacement of the standard.

xoutil.subprocess.call_and_check_output(args, *, stdin=None, shell=False)[source]

This function combines the result of both call and check_output (from the standard library module).

Returns a tuple (retcode, output, err_output).

xoutil.textwrap – Text wrapping and filling.

Text wrapping and filling.

xoutil.textwrap.dedent(text, skip_firstline=False)[source]

Remove any common leading whitespace from every line in text.

This can be used to make triple-quoted strings line up with the left edge of the display, while still presenting them in the source code in indented form.

Note that tabs and spaces are both treated as whitespace, but they are not equal: the lines "    hello" and "\thello" are considered to have no common leading whitespace.

If skip_firstline is True, the first line is separated from the rest of the body. This helps with docstrings that follow PEP 257.

Warning

The skip_firstline argument is missing in standard library.

xoutil.textwrap.indent(text, prefix, predicate=None)[source]

Adds ‘prefix’ to the beginning of selected lines in ‘text’.

If ‘predicate’ is provided, ‘prefix’ will only be added to the lines where ‘predicate(line)’ is True. If ‘predicate’ is not provided, it will default to adding ‘prefix’ to all non-empty lines that do not consist solely of whitespace characters.

Note

Backported from Python 3.3. In Python 3.3 this is an alias.

xoutil.threading – Higher-level threading interface

xoutil.threading.async_call(func, args=None, kwargs=None, callback=None, onerror=None)[source]

Executes a function func with the given positional and keyword arguments asynchronously.

If callback is provided, it is called with a single positional argument: the result of calling func(*args, **kwargs).

If the called function ends with an exception and onerror is provided, it is called with the exception object.

Returns:An event object that gets signalled when the function ends its execution whether normally or with an error.
Return type:threading.Event
xoutil.threading.sync_call(funcs, callback, timeout=None)[source]

Calls several functions each in it’s own thread, and waits for all to end.

Each time a function ends the callback is called (wrapped in a lock to avoid race conditions) with the result of the as a single positional argument.

If timeout is not None it sould be a float number indicading the seconds to wait before aborting. Functions that terminated before the timeout will have called callback, but those that are still working will be ignored.

Todo

Abort the execution of a thread.

Parameters:funcs – A sequences of callables that receive no arguments.

xoutil.types - Names for built-in types and extensions.

xoutil.types.is_iterable(maybe)[source]

Returns True if maybe is an iterable object (e.g. implements the __iter__ method):

>>> is_iterable('all strings are iterable')
True

# Numbers are not
>>> is_iterable(1)
False

>>> from xoutil.eight import range
>>> is_iterable(range(1))
True

>>> is_iterable({})
True

>>> is_iterable(tuple())
True

>>> is_iterable(set())
True
xoutil.types.is_collection(maybe)[source]

Test maybe to see if it is a tuple, a list, a set or a generator function.

It returns False for dictionaries and strings:

>>> is_collection('all strings are iterable')
False

# Numbers are not
>>> is_collection(1)
False

>>> from xoutil.eight import range
>>> is_collection(range(1))
True

>>> is_collection({})
False

>>> is_collection(tuple())
True

>>> is_collection(set())
True

>>> is_collection(a for a in range(100))
True

Changed in version 1.5.5: UserList are collections.

xoutil.types.is_scalar(maybe)[source]

Returns True if maybe is a string, an int, or some other scalar type (i.e not an iterable.)

xoutil.types.is_string_like(maybe)[source]

Returns True if maybe acts like a string

xoutil.types.is_module(maybe)[source]

Returns True if maybe is a module.

xoutil.types.is_classmethod(desc, name=Unset)[source]

Returns true if a method is a class method.

Parameters:
  • desc

    This may be the method descriptor or the class that holds the method, in the second case you must provide the name of the method.

    Note

    Notice that in the first case what is needed is the method descriptor, i.e, taken from the class’ __dict__ attribute. If instead you pass something like cls.methodname, this method will return False whilst is_instancemethod() will return True.

  • name – The name of the method, if the first argument is the class.
xoutil.types.is_staticmethod(desc, name=Unset)[source]

Returns true if a method is a static method.

This function takes the same arguments as is_classmethod().

xoutil.types.is_instancemethod(desc, name=Unset)[source]

Returns true if a given method is neither a static method nor a class method.

This function takes the same arguments as is_classmethod().

xoutil.types.is_slotwrapper(desc, name=Unset)[source]

Returns True if a given method is a slot wrapper (i.e. a method that is builtin in the object base class).

This function takes the same arguments as is_classmethod().

xoutil.types.are_instances(*subjects, types)[source]

Return True if every subject is an instance of (any) types.

Parameters:
  • subjects – All but last positional arguments. Are the objects required to be instances of types.
  • types – The last positional argument. Either a single type or a sequence of types. This must meet the conditions on the last argument of isinstance().
Returns:

True or False. True if for every subject, isinstance(subject, types) is True. Otherwise, False.

If no subjects are provided return True:

>>> are_instances(int)
True

See also

The function no_instances() allows to test for subjects not being instances of types.

xoutil.types.no_instances(*subjects, types)[source]

Return True if every subject is not an instance of (neither) types.

Parameters:
  • subjects – All but last positional arguments. Are the objects required not to be instances of types.
  • types – The last positional argument. Either a single type or a sequence of types. This must meet the conditions on the last argument of isinstance().
Returns:

True or False. True if for every subject, isinstance(subject, types) is False. Otherwise, False.

If no subjects are provided return True:

>>> no_instances(int)
True

Note

This is not the same as not are_instances(...).

This function requires that no subject is an instance of types. Negating are_instances() would be True if any subject is not an instance of types.

xoutil.types.new_class(name, bases=(), kwds=None, exec_body=None)[source]

Create a class object dynamically using the appropriate metaclass.

New in version 1.5.5.

xoutil.types.prepare_class(name, bases=(), kwds=None)[source]

Call the __prepare__ method of the appropriate metaclass.

Returns (metaclass, namespace, kwds) as a 3-tuple

metaclass is the appropriate metaclass namespace is the prepared class namespace kwds is an updated copy of the passed in kwds argument with any ‘metaclass’ entry removed. If no kwds argument is passed in, this will be an empty dict.

New in version 1.5.5.

xoutil.types.UnsetType

alias of Logical

xoutil.types.SlotWrapperType

alias of wrapper_descriptor

class xoutil.types.Required(*args, **kwargs)[source]

A type for required fields in scenarios where a default is not possible.

class xoutil.types.mro_dict(target)[source]

Utility behaving like a read-only dict of target MRO attributes.

For example:

>>> class A(object):
...     x = 12
...     y = 34

>>> class B(A):
...     y = 56
...     z = 78

>>> d = mro_dict(B)

>>> d['x']
12

>>> d['y']
56

>>> d['z']
78
class xoutil.types.MappingProxyType

New in version 1.5.5.

Read-only proxy of a mapping. It provides a dynamic view on the mapping’s entries, which means that when the mapping changes, the view reflects these changes.

Note

In Python 3.3+ this is an alias for types.MappingProxyType in the standard library.

class xoutil.types.SimpleNamespace[source]

New in version 1.5.5.

A simple object subclass that provides attribute access to its namespace, as well as a meaningful repr.

Unlike object, with SimpleNamespace you can add and remove attributes. If a SimpleNamespace object is initialized with keyword arguments, those are directly added to the underlying namespace.

The type is roughly equivalent to the following code:

class SimpleNamespace(object):
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)
    def __repr__(self):
        keys = sorted(self.__dict__)
        items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys)
        return "{}({})".format(type(self).__name__, ", ".join(items))
    def __eq__(self, other):
        return self.__dict__ == other.__dict__

SimpleNamespace may be useful as a replacement for class NS: pass. However, for a structured record type use namedtuple() instead.

Note

In Python 3.4+ this is an alias to types.SimpleNamespace.

class xoutil.types.DynamicClassAttribute(fget=None, fset=None, fdel=None, doc=None)[source]

Route attribute access on a class to __getattr__().

This is a descriptor, used to define attributes that act differently when accessed through an instance and through a class. Instance access remains normal, but access to an attribute through a class will be routed to the class’s __getattr__() method; this is done by raising AttributeError.

This allows one to have properties active on an instance, and have virtual attributes on the class with the same name (see Enum for an example).

New in version 1.5.5.

Note

The class Enum mentioned has not yet been backported.

Note

In Python 3.4+ this is an alias to types.DynamicClassAttribute.

Importing Unset and ignored

Warning

Removed in 1.5.0

Deprecated since version 1.4.0: These imports are removed in version 1.5.0.

The values Unset and ignored are not types neither functions that test for types. They are values and are moved out of this module. Nevertheless, they will remain importable from this module up to version 1.5.0.

xoutil.types.Unset

See UnsetType.

xoutil.types.ignored

To be used in arguments that are currently ignored cause they are being deprecated. The only valid reason to use ignored is to signal ignored arguments in method’s/function’s signature.

Other utils

xoutil.web.slugify(s, entities=True, decimal=True, hexadecimal=True)[source]

Normalizes string, converts to lower-case, removes non-alpha characters, and converts spaces to hyphens.

Parts from http://www.djangosnippets.org/snippets/369/

>>> slugify("Manuel Vázquez Acosta")    
'manuel-vazquez-acosta'

If s and entities is True (the default) all HTML entities are replaced by its equivalent character before normalization:

>>> slugify("Manuel V&aacute;zquez Acosta")   
'manuel-vazquez-acosta'

If entities is False, then no HTML-entities substitution is made:

>>> value = "Manuel V&aacute;zquez Acosta"
>>> slugify(value, entities=False)  
'manuel-v-aacute-zquez-acosta'

If decimal is True, then all entities of the form &#nnnn where nnnn is a decimal number deemed as a unicode codepoint, are replaced by the corresponding unicode character:

>>> slugify('Manuel V&#225;zquez Acosta')  
'manuel-vazquez-acosta'

>>> value = 'Manuel V&#225;zquez Acosta'
>>> slugify(value, decimal=False)  
'manuel-v-225-zquez-acosta'

If hexadecimal is True, then all entities of the form &#nnnn where nnnn is a hexdecimal number deemed as a unicode codepoint, are replaced by the corresponding unicode character:

>>> slugify('Manuel V&#x00e1;zquez Acosta')  
'manuel-vazquez-acosta'

>>> slugify('Manuel V&#x00e1;zquez Acosta', hexadecimal=False)  
'manuel-v-x00e1-zquez-acosta'
xoutil.uuid.uuid(*args, **kw)[source]

Return a “Global Unique ID” as a string.

Parameters:random – If True, a random uuid is generated (does not use host id).

xoutil.validators.identifiers – Simple identifiers validators

Regular expressions and validation functions for several identifiers.

xoutil.validators.identifiers.is_valid_identifier(name)[source]

Returns True if name a valid Python identifier.

Note

Only Python 2’s version of valid identifier. This means that some Python 3 valid identifiers are not considered valid. This helps to keep things working the same in Python 2 and 3.

xoutil.validators.identifiers.is_valid_full_identifier(name)[source]

Returns True if name is a valid dotted Python identifier.

See is_valid_identifier() for what “validity” means.

xoutil.validators.identifiers.is_valid_public_identifier(name)[source]

Returns True if name is a valid Python identifier that is deemed public.

Convention says that any name starting with a “_” is not public.

See is_valid_identifier() for what “validity” means.

Changelog

1.7 series

2017-10-31. Release 1.7.12
  • xoutil.datetime.EmptyTimeSpan is now pickable.
2017-10-05. 1.7.11
  • Fix bug #9: TimeSpans are not hashable.
2017-09-21. 1.7.10
2017-09-20. 1.7.9
2017-09-19. 1.7.8
  • Added module xoutil.dim – Facilities to work with concrete numbers.
2017-09-07. 1.7.7
2017-09-05. Release 1.7.6
  • Fix a bug in xoutil.datetime.TimeSpan for Python 2. Representing a time span might fail with a ‘Maximum Recursion Detected’ error.
2017-09-05. Release 1.7.5
2017-04-06. Release 1.7.4
2017-02-07. Release 1.7.2
2015-12-17. Release 1.7.1

Fixes in 1.7.1.post1:

Fixes in 1.7.1.post2:

Warning

Due to lack of time, we have decided to release this version without proper releases of 1.7.0 and 1.6.11.

Unreleased. Release 1.7.0

This release was mainly focused in providing a new starting point for several other changes. This release is being synchronized with the last release of the 1.6.11 to allow deprecation messages to be included properly.

The following is the list of changes:

1.6 series

Unreleased. Release 1.6.11

This is the last release of the 1.6 series. It’s being synchronized with release 1.7.0 to deprecate here what’s being changed there.

  • The defaults argument of xoutil.objects.smart_copy() is marked to be keyword-only in version 1.7.0.
  • Fixes a bug in xoutil.objects.smart_copy(). If defaults was None is was not being treated the same as being False, as documented. This bug was also fixed in version 1.7.0.
  • xoutil.objects.metaclass() will be moved to xoutil.eight.meta in version 1.7.0 and deprecated, it will be removed from xoutil.object in version 1.7.1.
  • This release will be the last to support Python 3.1, 3.2 and 3.3. Support will be kept for Python 2.7 and Python 3.4.
2015-04-15. Release 1.6.10
2015-04-03. Release 1.6.9
2015-01-26. Release 1.6.8
2014-12-17. Release 1.6.7
  • Added the strict argument to xoutil.records.datetime_reader().

  • You may now install xoutil[extra] so that not required but useful packages are installed when xoutil is installed.

    For now this only includes python-dateutil that allows the change in datetime_reader().

2014-11-26. Release 1.6.6
2014-10-13. Release 1.6.5
  • Added the module xoutil.records.

  • Deleted deprecated xoutil.compat.

  • Deprecate the xoutil.six. It will removed in 1.7.0 (probably next release).

    Now xoutil requires six 1.8.0.

2014-09-13. Release 1.6.4
2014-08-05. Release 1.6.3
2014-08-04. Release 1.6.2
2014-07-18. Release 1.6.1
2014-06-02. Release 1.6.0
  • Changes the signature of xoutil.names.nameof(), also the semantics of the full parameter is improved.

    This is the major change in this release. Actually, this release has being prepared in sync with the release 1.5.6 (just a few days ago) to have this change passed while still keeping our versions scheme.

1.5 series

2014-05-29. Release 1.5.6
2014-05-13. Release 1.5.5
2014-04-08. Release 1.5.4
  • Fix a bug in xoutil.objects.extract_attrs(). It was not raising exceptions when some attribute was not found and default was not provided.

    Also now the function supports paths like xoutil.objects.get_traverser().

  • xoutil contains now a copy of the excellent project six exported as xoutil.six (not documented here). Thus the compatibility module xoutil.compat is now deprecated and will removed in the future.

    There are some things that xoutil.compat has that xoutil.six does not. For instance, six does not include fine grained python version markers. So if your code depends not on Python 3 v Python 2 dichotomy but on features introduced in Python 3.2 you must use the sys.version_info directly.

    Notwithstanding that, xoutil will slowly backport several Python 3.3 standard library features to Python 2.7 so that they are consistently used in any Python up to 2.7 (but 3.0).

2014-04-01. Release 1.5.3
2014-03-03. Release 1.5.2
2014-01-24. Release 1.5.0

1.4 series

2013-04-26. Release 1.4.0

1.3 series

1.2 series

2013-04-03. Release 1.2.3
  • Bug fixes in xoutil.proxy and xoutil.aop.classical.
2013-03-25. Release 1.2.2
  • Adds xoutil.bases - Implementations of base 32 and base 64 (numeric) representations.
2013-02-14. Release 1.2.1
  • Loads of improvements for Python 3k compatibility: Several modules were fixed or adapted to work on both Python 2.7 and Python 3.2. They include (but we might have forgotten some):
  • Rescued xoutil.annotate and is going to be supported from now on.
  • Introduced module xoutil.subprocess and function xoutil.subprocess.call_and_check_output().
  • Introduced module xoutil.decorator.compat that enables constructions that are interoperable in Python 2 and Python 3.
  • Introduced xoutil.iterators.zip(), xoutil.iterators.izip(), xoutil.iterators.map(), and xoutil.iterators.imap().
2013-01-04. Release 1.2.0

This is the first of the 1.2.0 series. It’s been given a bump in the minor version number because we’ve removed some deprecated functions and/or modules.

  • Several enhancements to xoutil.string to make it work on Python 2.7 and Python 3.2.

    Deprecates xoutil.string.normalize_to_str() in favor of the newly created xoutil.string.force_str() which is Python 3 friendly.

  • Backwards incompatible changes in xoutil.objects API. For instance, replaces getattr parameter with getter in xoutil.objects.xdir() and co.

  • Extracts decorator-making facilities from xoutil.decorators into xoutil.mdeco.

  • Fixes in xoutil.aop.extended. Added parameters in xoutil.aop.classical.weave().

  • Introduces xoutil.iterators.first_n() and deprecates xoutil.iterators.first() and xoutil.iterators.get_first().

  • Removes the zope.interface awareness from xoutil.context since it contained a very hard to catch bug. Furthermore, this was included to help the implementation of xotl.ql, and it’s no longer used there.

    This breaks version control policy since it was not deprecated beforehand, but we feel it’s needed to avoid spreading this bug.

  • Removed long-standing deprecated modules xoutil.default_dict, xoutil.memoize and xoutil.opendict.

  • Fixes bug in xoutil.datetime.strfdelta(). It used to show things like ‘1h 62min’.

  • Introduces xoutil.compat.class_type that holds class types for Python 2 or Python 3.

1.1 series

2012-11-01. Release 1.1.4
  • Introduces xoutil.compat.iteritems_(), xoutil.compat.iterkeys_() and xoutil.compat.itervalues_().
  • execution context are now aware of zope.interface interfaces; so that you may ask for a context name implementing a given interface, instead of the name itself.
  • Improves xoutil.formatter documentation.
  • Several fixes to xoutil.aop.classical. It has sudden backwards incompatible changes.
  • before and after methods may use the *args, **kwargs idiom to get the passed arguments of the weaved method.
  • Several minor fixes: Invalid warning about Unset not in xoutil.types
2012-08-22. Release 1.1.3
2012-07-11. Release 1.1.2
  • Fixes all copyrights notices and chooses the PSF License for Python 3.2.3 as the license model for xoutil releases.
  • All releases from now on will be publicly available at github.
2012-07-06. Release 1.1.1
  • Improves deprecation warnings by pointing to the real calling filename
  • Removes all internal use of simple_memoize since it’s deprecated. We now use lru_cache().
2012-07-03. Release 1.1.0

1.0 series

2012-06-15. Release 1.0.30
  • Introduces a new module xoutil.proxy.
  • Starts working on the sphinx documentation so that we move to 1.1 release we a decent documentation.
2012-06-01. Release 1.0.29.
  • Introduces xoutil.iterators.slides and xoutil.aop.basic.contextualized
2012-05-28. Release 1.0.28.
  • Fixes normalize path and other details
  • Makes validate_attrs to work with mappings as well as objects
  • Improves complementors to use classes as a special case of sources
  • Simplifies importing of legacy modules
  • PEP8
2012-05-22. Release 1.0.27.
  • Removes bugs that were not checked (tested) in the previous release.
2012-05-21. Release 1.0.26.
  • Changes in AOP classic. Now you have to rename after, before and around methods to _after, _before and _around.

    It is expected that the signature of those methods change in the future.

  • Introducing a default argument for xoutil.objects.get_first_of().

  • Other minor additions in the code. Refactoring and the like.

2012-04-30. Release 1.0.25.
  • Extends the classical AOP approach to modules. Implements an extended version with hooks.
  • 1.0.25.1: Makes classical/extended AOP more reliable to TypeError’s in getattr. xoonko, may raise TypeError’s for TranslatableFields.

2012-04-27. Release 1.0.24.

  • Introduces a classical AOP implementation: xoutil.aop.classical.
2012-04-10. Release 1.0.23.
  • Introduces decorators: xoutil.decorators.instantiate and xoutil.aop.complementor
2012-04-05. Release 1.0.22
  • Allows annotation’s expressions to use defined local variables. Before this release the following code raised an error:

    >>> from xoutil.annotate import annotate
    >>> x1 = 1
    >>> @annotation('(a: x1)')
    ... def dummy():
    ...     pass
    Traceback (most recent call last):
       ...
    NameError: global name 'x1' is not defined
    
  • Fixes decorators to allow args-less decorators

2012-04-03. Release 1.0.21
  • Includes a new module xoutil.annotate that provides a way to place Python annotations in forward-compatible way.

Work plans (backlogs)

See at the end: All pending tasks listed.

Python 2 and 3 Compatibility

Migrate features compliant with six concept to xoutil.eight

Todo

Check all around xoutil and migrate to xoutil.eight all tools that are related to write code that will be compatible with Python 2 and 3 versions.

This task was already started by migrating:

Todo

Progressively update HISTORY file with all xoutil.eight related progress.

All pending tasks listed

Todo

Check all around xoutil and migrate to xoutil.eight all tools that are related to write code that will be compatible with Python 2 and 3 versions.

(The original entry is located in backlog/backlog-six2eight.rst, line 4.)

Todo

Progressively update HISTORY file with all xoutil.eight related progress.

(The original entry is located in backlog/backlog-six2eight.rst, line 16.)

Todo

Check all around xoutil and migrate to xoutil.eight all tools that are related to write code that will be compatible with Python 2 and 3 versions.

(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/xoutil/checkouts/releases-1.7.x/docs/source/backlog/backlog-six2eight.rst, line 4.)

Todo

Progressively update HISTORY file with all xoutil.eight related progress.

(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/xoutil/checkouts/releases-1.7.x/docs/source/backlog/backlog-six2eight.rst, line 16.)

Todo

Abort the execution of a thread.

(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/xoutil/checkouts/releases-1.7.x/xoutil/threading.py:docstring of xoutil.threading.sync_call, line 12.)

How to contribute to xoutil

Testing

Running tests

xoutil uses pytest and tox for tests. We have a bundled version of pytest in the runtests.py scripts so for running tests in your environment you don’t really have to install pytest and/or tox.

Given you have installed xoutil as development user-local package with:

$ python setup.py develop --user

You may run the tests with:

$ python runtests.py

Use the -h to show the pytest command line options.

If you have tox installed, then should have also Python 2.7, Python 3.2 and PyPy interpreters installed and in your path to run the tests with tox. Having done so, you may run the tests with:

$ tox

This will run the tests suite in those three environments.

Writing tests

Testing was not introduced in xoutil until late in the project life. So there are many modules that lack a proper test suite.

To ease the task of writing tests, we chose pytest.

We use both normal tests (“à la pytest”) and doctest. The purpose of doctests is testing the documentation instead of testing the code, which is the purpose of the former.

Most of our normal tests are currently simple functions with the “test_” prefix and are located in the tests/ directory.

Many functions that lacks are, though, tested by our use in other projects. However, it won’t hurt if we write them.

Documentation

Since xoutil is collection of very disparate stuff, the documentation is hardly narrative but is contained in the docstrings of every “exported” element, except perhaps for module-level documentation in some cases. In these later cases, a more narrative text is placed in the .rst file that documents the module.

Versioning and deprecation

xoutil uses three version components.

The first number refers to language compatibility: xoutil 1.x series are devoted to keeping compatible versions of the code for both Python 2.7 and Python 3.2+. The jump to 2.x version series will made when xoutil won’t support Python 2.7 any longer.

The second number is library major version indicator. This indicates, that some deprecated stuff are finally removed and/or new functionality is provided.

The third number is minor release number. Devoted to indicate mostly fixes to existing functionality. Though many times, some functions are merged and the old ones get a deprecation warning.

Occasionally, a fourth component is added to a release. This usually means a packaging problem, or bug in the documentation.

Module layout and rules

Many modules in xoutil contains definitions used in xoutil itself. Though we try to logically place every feature into a rightful, logical module; sometimes this is not possible because it would lead to import dependency cycles.

We are establishing several rules to keep our module layout and dependency quite stable while, at the same time, allowing developers to use almost every feature in xoutil.

We divide xoutil modules into 4 tiers:

  1. Tier 0

    This tier groups the modules that must not depend from other modules besides the standard library. These modules implement some features that are exported through other xoutil modules. These module are never documented, but their re-exported features are documented elsewhere. For instance, xoutil.type.UnsetType is actually implemented in xoutil._values.

    Also the exported module xoutil.compat is this tier.

  2. Tier 1

    In this tier we have:

    • xoutil.decorator.meta. This is to allow the definition of decorators in other modules.
    • xoutil.names. This is to allow the use of xoutil.names.namelist for the __all__ attribute of other modules.
    • xoutil.deprecation. It must not depend on any other module besides xoutil.compat. Many modules in xoutil will use this module at import time to declare deprecated features.
  3. Tier 2

    Modules in this tier should depend only on features defined in tiers 0 and 1 modules, and that export features that could be imported at the module level.

    This tier only has the xoutil.modules. Both xoutil.modules.modulepropery() and xoutil.modules.modulemethod() are meant be used at module level definitions, so they are likely to be imported at module level.

  4. Tier 3

    The rest of the modules.

    In this tier, xoutil.objects and xoutil.types are kings. But in order to allow the import of other modules the following pair of rules are placed:

  • At the module level only import from upper tiers.
  • Imports from tier 3 are allowed, but only inside the functions that use them.

This entails that you can’t define a function that must be a module level import, like a decorator for other functions. For that reason, decorators are mostly placed in the xoutil.decorator module.

The tiers above are a “logical suggestion” of how xoutil modules are organized and indicated how they might evolve.

List of contributors

If you’re a contributor and you’re not listed here, we appologize for that omission, and ask you to add yourself to the list.

  • Medardo Rodríguez started this package and wrote most of it.
  • Dunia Trujillo has fixed bugs, tested the software and also contributed code.
  • Manuel Vázquez has contribute code and reorganize the package for the 1.1.x release series. He has contributed also to the documentation and docstring in reST format with doctests.

Indices and tables