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'>
- signature –
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.
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 astimed(n)
.until(times=n)
is the same astimes(n)
.until(pred=func, skipargs=skip)
is the same aspred(func, skipargs=skip)
.until(errors=errors, **kwargs)
is the same asuntil_errors(*errors, **kwargs)
.until(accumulate=mass, path=path, initial=initial)
is the same asaccumulated(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.
- First it will be called its
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()
andwhenany()
call theclose()
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 thetimes
function.We also get the generator from the unbounded function.
Then we call
next(boundary)
to allow thetimes
boundary to initialize itself. This runs the code of thetimes
definition up to the line 5 (the firstyield
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 thatn
. 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 thatn
. If passes has reachedn
, 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 theclose()
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:
- 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). - A looping structure that tests the condition has not been met and yields False at each cycle.
- 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 bygenerate()
.This class is actually subclassed inside the
apply()
so that the weaving boundary definition with the target unbounded function is not exposed.
-
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 theBounded
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 aBounded
instance.Then we define an execute function bounded by timed. This function melds the
receive
andsend
processes, but we can’t actually callsend
because we need to yield after each message has been received or sent. That’s why we need to call thegenerate()
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”.-
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
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.
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.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 callingpop()
.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 callingpop()
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}
-
-
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. Ifm
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 tod.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.
-
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 ofFalse
.
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
- A type (or tuple of types) to test with
xoutil.context
- Simple execution contexts¶
A context manager for execution context flags.
-
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):
Generate the same pass-phrase, just removing invalid characters and converting the result to lower-case.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>”).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 isNone
for level zero or negative, size4
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.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.
-
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
orunbount into the future
or both.
-
valid
¶ A bound time span is valid if it starts before it ends.
Unbound time spans are always valid.
-
__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.
-
-
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.
-
classmethod
-
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.
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 toUNIT
.
-
_unit_
¶ The canonical
quantity
. This is the quantity 1 (UNIT
) expressed in terms of the canonical unit.
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 beEffort.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'
-
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.
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.
Other attributes:
-
kilometre
¶
-
km
¶
-
centimetre
¶
-
cm
¶
-
millimetre
¶
-
mm
¶
-
nanometre
¶
-
nm
¶
-
-
class
xoutil.dim.base.
Time
[source]¶ The Time base quantity.
-
second
¶ The canonical unit.
Other attributes:
-
millisecond
¶
-
ms
¶
-
nanosecond
¶
-
ns
¶
-
minute
¶
-
hour
¶
-
-
class
xoutil.dim.base.
Mass
[source]¶ The Mass base quantity.
-
kilogram
¶ The canonical unit.
Other attributes:
-
gram
¶
-
-
class
xoutil.dim.base.
ElectricCurrent
[source]¶ The electrical current base quantity.
-
ampere
¶ The canonical unit.
-
-
class
xoutil.dim.base.
Temperature
[source]¶ The thermodynamic temperature base quantity.
-
kelvin
¶ The canonical unit.
-
Aliases¶
-
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.
J
¶ An alias of
Luminosity
Derived quantities¶
-
class
xoutil.dim.base.
Frequency
¶ Defined as
T
**-1
(which is the same as1/T
).-
unit_per_second
¶ The canonical unit.
Aliases of the canonical unit:
-
Hz
¶
-
-
class
xoutil.dim.base.
Force
¶ -
-
metre_kilogram_per_second_squared
¶ The canonical unit.
Aliases of the canonical unit:
-
N
¶
-
Newton
¶
-
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 ofA
and b, the canonical unit ofB
.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_aA**n
; whenn=1
this is the same asA
; whenn=2
this is the same asA * A
; for other positive values ofn
, the canonical unit name is a_pow_n; for negative values ofn
is the same as1/A**n
; forn=0
this is theScalar
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.
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.
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 themetaclass()
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!See also
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
oropen()
built-in).
New in version 1.7.0.
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.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.
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:
- a file-like object (ready to be passed to
shutil.copyfileobj()
) - a string, the file path.
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.
- a file-like object (ready to be passed to
-
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), anOSError
is raised if the target directory already exists.Note
makedirs()
will become confused if the path elements to create includeos.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 originalos.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)
andf(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 tolambda _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.
- name: The name of the function (
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 ABCInfinityComparable
.
-
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
, anddatetime.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)
; wherexs
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 firstxs
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
andy
are considered equal (duplicates) ifkey(x) == key(y)
. By default key is the identity function.Works with any sequence that supports
len()
,__getitem__()
andaddition
.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 builtinzip
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 builtinmap
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 exampleislice()
ortakewhile()
). If not specified, fillvalue defaults to None.This function is actually an alias to
itertools.izip_longest()
in Python 2.7, and an alias toitertools.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 tofp
(a.write()
-supporting file-like object).If
skipkeys
is true thendict
keys that are not basic types (str
,unicode
,int
,long
,float
,bool
,None
) will be skipped instead of raising aTypeError
.If
ensure_ascii
is true (the default), all non-ASCII characters in the output are escaped with\uXXXX
sequences, and the result is astr
instance consisting of ASCII characters only. Ifensure_ascii
isFalse
, some chunks written tofp
may beunicode
instances. This usually happens because the input contains unicode strings or theencoding
parameter is used. Unlessfp.write()
explicitly understandsunicode
(as incodecs.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 anOverflowError
(or worse).If
allow_nan
is false, then it will be aValueError
to serialize out of rangefloat
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 whenindent
is specified. You can useseparators=(',', ': ')
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 thecls
kwarg; otherwiseJSONEncoder
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 formattedstr
.If
skipkeys
is true thendict
keys that are not basic types (str
,unicode
,int
,long
,float
,bool
,None
) will be skipped instead of raising aTypeError
.If
ensure_ascii
is false, all non-ASCII characters are not escaped, and the return value may be aunicode
instance. Seedump
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 anOverflowError
(or worse).If
allow_nan
is false, then it will be aValueError
to serialize out of rangefloat
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 whenindent
is specified. You can useseparators=(',', ': ')
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 thecls
kwarg; otherwiseJSONEncoder
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 appropriateencoding
name must be specified. Encodings that are not ASCII based (such as UCS-2) are not allowed, and should be wrapped withcodecs.getreader(fp)(encoding)
, or simply decoded to aunicode
object and passed toloads()
object_hook
is an optional function that will be called with the result of any object literal decode (adict
). The return value ofobject_hook
will be used instead of thedict
. 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 ofobject_pairs_hook
will be used instead of thedict
. 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). Ifobject_hook
is also defined, theobject_pairs_hook
takes priority.To use a custom
JSONDecoder
subclass, specify it with thecls
kwarg; otherwiseJSONDecoder
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
(astr
orunicode
instance containing a JSON document) to a Python object.If
s
is astr
instance and is encoded with an ASCII based encoding other than utf-8 (e.g. latin-1) then an appropriateencoding
name must be specified. Encodings that are not ASCII based (such as UCS-2) are not allowed and should be decoded tounicode
first.object_hook
is an optional function that will be called with the result of any object literal decode (adict
). The return value ofobject_hook
will be used instead of thedict
. 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 ofobject_pairs_hook
will be used instead of thedict
. 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). Ifobject_hook
is also defined, theobject_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 thecls
kwarg; otherwiseJSONDecoder
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 correspondingfloat
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
(astr
orunicode
instance containing a JSON document)
-
raw_decode
(s, idx=0)[source]¶ Decode a JSON document from
s
(astr
orunicode
beginning with a JSON document) and return a 2-tuple of the Python representation and the index ins
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 foro
if possible, otherwise it should call the superclass implementation (to raiseTypeError
).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.
- source –
-
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.ecustomize()
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.
-
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, andtyped=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 ofgetattr
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.
-
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 thatattr_filter(attr)
andvalue_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_filter – optional A filter for attribute names. Deprecated since 1.4.1
- value_filter – optional A filter for attribute values. Deprecated since 1.4.1
- getter – optional 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:
- It’s the value
xoutil.types.Required
or an instance of Required. - An exception object
- A sequence with is first value being a subclass of Exception. In which
case
xoutil.data.adapt_exception
is used.
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 ofgetattr()
.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 bothoperator.itergetter()
andoperator.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
orsets
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.
- Any two
-
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_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.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.
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.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.- format – The format the datetime is expected to be in the external
data. This is passed to
-
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.
date_reader
(format, nullable=False, default=None, strict=True)[source]¶ Return a date reader.
This is similar to
datetime_reader()
but instead of returning adatetime.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.
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.
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()
orsafe_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_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 oldinvalid_underscore
argument.False
orNone
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.
- 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
-
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.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.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_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 whilstis_instancemethod()
will return True. - name – The name of the method, if the first argument is the class.
- desc –
-
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
, withSimpleNamespace
you can add and remove attributes. If aSimpleNamespace
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 forclass NS: pass
. However, for a structured record type usenamedtuple()
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.
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ázquez Acosta") 'manuel-vazquez-acosta'
If entities is False, then no HTML-entities substitution is made:
>>> value = "Manuel Vá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ázquez Acosta') 'manuel-vazquez-acosta' >>> value = 'Manuel Vá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ázquez Acosta') 'manuel-vazquez-acosta' >>> slugify('Manuel Vá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-09-21. 1.7.10¶
- Fix bug #6:
TimeSpan.overlaps
was incorrectly defined. - Fix bug #5:
TimeSpan
can’t have a union method.
2017-09-20. 1.7.9¶
- Deprecate
xoutil.dim.meta.Signature.isunit()
. - Rename
xoutil.dim.meta.QuantityType
toxoutil.dim.meta.Dimension
. - Fix bug in
xoutil.datetime.TimeSpan
.start_date
andend_date
now return an instance of Python’sdatetime.date
instead of a sub-class.
2017-09-19. 1.7.8¶
- Added module
xoutil.dim
– Facilities to work with concrete numbers.
2017-09-07. 1.7.7¶
- Fixed bug in
xoutil.datetime.date
that prevented to usestrftime()
in subclasses. - Fixed bug in
xoutil.datetime.TimeSpan.valid()
.
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¶
- Added
xoutil.datetime.TimeSpan
. - Added the module
xoutil.infinity
. - Added the keyword argument on_error to
xoutil.bound.until_errors()
.
2017-04-06. Release 1.7.4¶
- Added the argument key to
xoutil.iterators.delete_duplicates()
. - Added the function
xoutil.iterators.iter_delete_duplicates()
.
2017-02-23. Release 1.7.3¶
2017-02-07. Release 1.7.2¶
- Add
xoutil.bound.until()
andxoutil.bound.until_errors()
. - Fix issue that made
xoutil.uuid
unusable. Introduced in version 1.7.1, commit 58eb359. - Remove support for Python 3.1 and Python 3.2.
2015-12-17. Release 1.7.1¶
- Add
xoutil.collections.PascalSet
andxoutil.collections.BitPascalSet
. - Add
xoutil.functools.lwraps()
. - Add
xoutil.objects.multi_getter()
,xoutil.objects.get_branch_subclasses()
,xoutil.objects.fix_method_documentation()
. - Add
xoutil.string.safe_str()
- Remove long deprecated modules:
xoutil.aop
andxoutil.proxy
. - Deprecate
xoutil.html
entirely. - The following modules are included on a provisional basis. Backwards incompatible changes (up to and including removal of the module) may occur if deemed necessary by the core developers:
Fixes in 1.7.1.post1:
Fix issue with both
xoutil.string.safe_decode()
andxoutil.string.safe_encode()
.Previously, the parameter encoding could contain an invalid encoding name and the function could fail.
Fixes in 1.7.1.post2:
Fix
xoutil.string.cut_suffix()
. The following invariant was being violated:>>> cut_suffix(v, '') == v # for any value of 'v'
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:
- The defaults
xoutil.objects.smart_copy()
has being made keyword only. - Deprecates the
pop()
semantics, they shadow thedict.pop()
. A newpop_level()
is provided to explicitly pop a stack level. The same is done for thepop()
method. - Deprecates
xoutil.iterators.fake_dict_iteritems()
. - Deprecates
xoutil.objects.metaclass
in favor forxoutil.eight.meta.metaclass()
.
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 fromxoutil.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¶
- Fix
repr()
andstr()
issues withxoutil.cli.Command
instances.
2015-04-03. Release 1.6.9¶
- The defaults argument in
xoutil.objects.smart_copy()
is now keyword-only. xoutil.context
is now greenlet-safe without depending of gevent.
2015-01-26. Release 1.6.8¶
- Added
xoutil.records.date_reader()
. - Added a forward-compatible
xoutil.inspect.getfullargspec()
. - Now
contexts
will support gevent-locals if available. See the note in the module documentation. - Minor fixes.
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 indatetime_reader()
.
2014-11-26. Release 1.6.6¶
- Improved the
xoutil.string.normalize_slug()
by providing both valids and invalids chars. - Added the
xoutil.string.normalize_ascii()
.
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¶
- Fix bug in
xoutil.fs.concatfiles()
: There were leaked opened files.
2014-08-05. Release 1.6.3¶
- Added the pre-release version of
xoutil.bound
module.
2014-08-04. Release 1.6.2¶
Fix encoding issues in
xoutil.string.cut_prefix()
andxoutil.string.cut_suffix()
.Previously this code failed:
>>> from xoutil.string import cut_prefix >>> cut_prefix(u'-\xe1', '-') Traceback ... ... UnicodeEncodeError: 'ascii' ...
Now both functions force its second argument to be of the same type of the first. See
xoutil.string.safe_decode()
andxoutil.string.safe_encode()
.
2014-07-18. Release 1.6.1¶
- Added the yield parameter in
xoutil.fs.ensure_filename()
. - Added the base parameter in
xoutil.modules.moduleproperty()
. - Added the function
xoutil.fs.concatfiles()
.
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¶
- Warn about a future backwards incompatible change in the behavior of
xoutil.names.nameof()
.
2014-05-13. Release 1.5.5¶
UserList are now collections in the sense of
xoutil.types.is_collection()
.Python 3.4 added to the list of tested Python environments. Notice this does not makes any warrants about identical behavior of things that were previously backported from Python 3.3.
For instance, the
xoutil.collections.ChainMap
has been already backported from Python 3.4, so it will have the same signature and behavior across all supported Python versions.But other new things in Python 3.4 are not yet backported to xoutil.
Now
xoutil.objects.metaclass()
supports the__prepare__
classmethod of metaclasses. This is fully supported in Python 3.0+ and partially mocked in Python 2.7.Backported
xoutil.types.MappingProxyType
from Python 3.3.Backported
xoutil.types.SimpleNamespace
from Python 3.4.Backported
xoutil.types.DynamicClassAttribute
from Python 3.4Added function
xoutil.iterators.delete_duplicates()
.Added parameter ignore_underscore to
xoutil.string.normalize_slug()
.Added module
xoutil.crypto
with a function for generating passwords.Fixed several bug in
xoutil.functools.compose()
.Makes
xoutil.fs.path.rtrim()
have a default value for the amount of step to traverse.
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 modulexoutil.compat
is now deprecated and will removed in the future.There are some things that
xoutil.compat
has thatxoutil.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 thesys.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¶
Now xoutil supports Python 2.7, and 3.1+. Python 3.0 was not tested.
Added a strict parameter to
xoutil.objects.smart_getter()
.New function
xoutil.objects.get_traverser()
.The function
xoutil.cli.app.main()
prefers its default parameter instead of the application’s default command.Allow the
xoutil.cli.Command
to define acommand_cli_name
to change the name of the command. Seexoutil.cli.tools.command_name()
.
2014-03-03. Release 1.5.2¶
Deprecated function
xoutil.objects.get_and_del_key()
. Use thedict.pop()
directly.To have consistent naming, renamed
get_and_del_attr()
andget_and_del_first_of()
topopattr()
andpop_first_of()
. Old names are left as deprecated aliases.Now
xoutil.functools.update_wrapper()
,xoutil.functools.wraps()
andxoutil.functools.lru_cache()
are Python 3.3 backports (or aliases).New module
xoutil.textwrap
.
2014-02-14. Release 1.5.1¶
- Added functions
xoutil.objects.dict_merge()
,xoutil.types.are_instances()
andxoutil.types.no_instances()
. - Deprecated function
xoutil.objects.smart_getattr()
. Usexoutil.objects.get_first_of()
instead.
2014-01-24. Release 1.5.0¶
- Lots of removals. Practically all deprecated since 1.4.0 (or before). Let’s
list a few but not all:
- Both
xoutil.Unset
andxoutil.Ignored
are no longer re-exported inxoutil.types
. - Removes module
xoutil.decorator.compat
, since it only contained the deprecated decoratorxoutil.decorator.compat.metaclass()
in favor ofxoutil.objects.metaclass()
. - Removes
nameof
andfull_nameof
fromxoutil.objects
in favor ofxoutil.names.nameof()
. - Removes
pow_
alias ofxoutil.functools.power()
. - Removes the deprecated
xoutil.decorator.decorator
function. Usexoutil.decorator.meta.decorator()
instead. - Now
get_module_path()
is documented and in modulexoutil.modules
.
- Both
- Also we have documented a few more functions, including
xoutil.fs.path.rtrim()
. - All modules below
xoutil.aop
are in risk and are being deprecated.
1.4 series¶
The fill argument in function
xoutil.iterators.slides()
now defaults to None. This is consistent with the intended usage ofUnset
and with the semantics of bothxoutil.iterators.continuously_slides()
andxoutil.iterators.first_n()
.Unset, as a default value for parameters, is meant to signify the absence of an argument and thus only would be valid if an absent argument had some kind of effect different from passing the argument.
Changes
xoutil.modules.customize()
API to separate options from custom attributes.Includes a random parameter to
xoutil.uuid.uuid()
.
- Deprecations and introductions:
- Importing
xoutil.Unset
andxoutil.Ignored
fromxoutil.types
now issues a warning. - New style for declaring portable metaclasses in
xoutil.objects.metaclass()
, soxoutil.decorator.compat.metaclass()
is now deprecated. - Adds the module
xoutil.pprint
and functionxoutil.pprint.ppformat()
. - Adds the first version of package
xoutil.cli
. - Adds the filter parameter to functions
xoutil.objects.xdir()
andxoutil.objects.fdir()
and deprecates attr_filter and value_filter. - Adds functions
xoutil.objects.attrclass()
,xoutil.objects.fulldir()
. - Adds function
xoutil.iterators.continuously_slides()
. - Adds package
xoutil.threading
. - Adds package
xoutil.html
and begins the port ofxoutil.html.parser
from Python 3.3 to xoutil, so that a common implementation for both Python 2.7 and Python 3.3 is available.
- Importing
- Bug fixes:
- Fixes some errors with
classical
AOP weaving of functions in modules that wherecustomized
. - Fixes bugs with
xoutil.modules
: makesxoutil.modules.modulemethod()
to customize the module, and improves performance.
- Fixes some errors with
2013-04-26. Release 1.4.0¶
- Refactors
xoutil.types
as explained in Importing Unset and ignored. - Changes involving
xoutil.collections
:- Moves SmartDict and SortedSmartDict from
xoutil.data
toxoutil.collections
. They are still accessible fromxoutil.data
. - Also there is now a
xoutil.collections.SmartDictMixin
that implements the update behind all smart dicts in xoutil. xoutil.collections.StackedDict
in now a SmartDict and thus gains zero-level initialization data.
- Moves SmartDict and SortedSmartDict from
- Removals of deprecated, poorly tested, or incomplete features:
- Removes deprecated
xoutil.decorators
. Usexoutil.decorator
. - Removed
xoutil.iterators.first()
, andxoutil.iterators.get_first()
. - Removed
xoutil.string.names()
,xoutil.string.normalize_to_str()
andxoutil.string.normalize_str_collection()
.
- Removes deprecated
- Newly deprecated functions:
- Deprecates
xoutil.iterators.obtain()
. - Deprecates
xoutil.iterators.smart_dict()
andxoutil.data.smart_copy()
in favor ofxoutil.objects.smart_copy()
.
- Deprecates
- New features:
- Introduces
xoutil.iterators.first_non_null()
. - Adds
xoutil.objects.copy_class()
and updatesxoutil.decorator.compat.metaclass()
to use it.
- Introduces
- Fixes a bug with
xoutil.deprecation.deprecated()
when used with classes: It changed the hierarchy and provoked infinite recursion in methods that use super.
1.3 series¶
Removes deprecated module
xoutil.mdeco
.xoutil.context.Context
now inherit from the newly created stacked dict classxoutil.collections.StackedDict
. Whenever you enter a context a new level of the stacked dict ispushed
, when you leave the context a level ispoped
.This also removes the data attribute execution context used to have, and, therefore, this is an incompatible change.
Introduces
xoutil.collections.OpenDictMixin
andxoutil.collections.StackedDict
.Fixes a bug in
xoutil.decorator.compat.metaclass()
: Slots were not properly handed.Fixes a bug with the simple
xoutil.collections.opendict
that allowed to shadow methods (even __getitem__) thus making the dict unusable.
1.2 series¶
2013-04-03. Release 1.2.3¶
- Bug fixes in
xoutil.proxy
andxoutil.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):
xoutil.context
xoutil.aop.basic
xoutil.deprecation
xoutil.proxy
- Rescued
xoutil.annotate
and is going to be supported from now on. - Introduced module
xoutil.subprocess
and functionxoutil.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()
, andxoutil.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 createdxoutil.string.force_str()
which is Python 3 friendly.Backwards incompatible changes in
xoutil.objects
API. For instance, replaces getattr parameter with getter inxoutil.objects.xdir()
and co.Extracts decorator-making facilities from
xoutil.decorators
intoxoutil.mdeco
.Fixes in
xoutil.aop.extended
. Added parameters inxoutil.aop.classical.weave()
.Introduces
xoutil.iterators.first_n()
and deprecatesxoutil.iterators.first()
andxoutil.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
andxoutil.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_()
andxoutil.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¶
- Adds function
xoutil.fs.rmdirs()
that removes empty dirs. - Adds functions
xoutil.string.safe_join()
,xoutil.string.safe_encode()
,xoutil.string.safe_decode()
, andxoutil.string.safe_strip()
; and the classxoutil.string.SafeFormatter
. - Adds function
xoutil.cpystack.iter_frames()
.
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¶
- Created the whole documentation Sphinx directory.
- Removed xoutil.future since it was not properly tested.
- Removed xoutil.annotate, since it’s not portable across Python’s VMs.
- Introduced module
xoutil.collections
- Deprecated modules
xoutil.default_dict
,xoutil.opendict
in favor ofxoutil.collections
. - Backported
xoutil.functools.lru_cache()
from Python 3.2. - Deprecated module
xoutil.memoize
in favor ofxoutil.functools.lru_cache()
.
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:
- Meta-classes definition. See
xoutil.eight.meta.metaclass()
. - Definitions that exists in only one version (2 or 3) of Python module
types
. Seexoutil.eight.types
.
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:
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 inxoutil._values
.Also the exported module
xoutil.compat
is this tier.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 ofxoutil.names.namelist
for the__all__
attribute of other modules.xoutil.deprecation
. It must not depend on any other module besidesxoutil.compat
. Many modules in xoutil will use this module at import time to declare deprecated features.
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
. Bothxoutil.modules.modulepropery()
andxoutil.modules.modulemethod()
are meant be used at module level definitions, so they are likely to be imported at module level.Tier 3
The rest of the modules.
In this tier,
xoutil.objects
andxoutil.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.
Copyright and Licence¶
Copyright (c) 2015 Merchise and Contributors.
Copyright (c) 2013, 2014 Merchise Autrement and Contributors.
Copyright (c) 2012 Medardo Rodríguez.
This software is released under terms similar to the Python Software Foundation (PSF) licence for Python 3.2 as stated below.
Three modules inside this package are backports from Python 3.2.3’s standard library and the PSF retains the copyright.
License Terms¶
This LICENSE AGREEMENT is between the Copyright Owner (Owner or Author), and the Individual or Organization (“Licensee”) accessing and otherwise using xoutil 1.7.12 software in source or binary form and its associated documentation.
Subject to the terms and conditions of this License Agreement, the Owner hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use xoutil 1.7.12 alone or in any derivative version, provided, however, that Owner’s License Agreement and Owner’s notice of copyright, i.e., “Copyright (c) 2015 Merchise and Contributors” are retained in xoutil 1.7.12 alone or in any derivative version prepared by Licensee.
In the event Licensee prepares a derivative work that is based on or incorporates xoutil 1.7.12 or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to xoutil 1.7.12.
The Owner is making xoutil 1.7.12 available to Licensee on an “AS IS” basis. THE OWNER MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, THE OWNER MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF xoutil 1.7.12 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
THE OWNER SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF xoutil 1.7.12 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING xoutil 1.7.12, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
This License Agreement will automatically terminate upon a material breach of its terms and conditions.
Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between The Owner and Licensee. This License Agreement does not grant permission to use The Owner trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party.
By copying, installing or otherwise using xoutil 1.7.12, Licensee agrees to be bound by the terms and conditions of this License Agreement.