Installation¶
From the cheeseshop¶
To install inxs, run this command in your terminal:
$ pip install inxs
This is the preferred method to install inxs, as it will always install the most recent stable release.
If you don’t have pip installed, this Python installation guide can guide you through the process.
From the sources¶
The sources for inxs can be downloaded from the Github repo.
You can either clone the public repository:
$ git clone git://github.com/funkyfuture/inxs
Or download the tarball:
$ curl -OL https://github.com/funkyfuture/inxs/tarball/master
Once you have a copy of the source, you can install it with:
$ python setup.py install
Or install an editable instance:
$ python setup.py develop
Usage¶
inxs
is designed to allow Pythonistas and other cultivated folks to write sparse and readable
transformations that take delb objects as input. Most likely they will return the same, but
there’s no limitation into what the data can be mangled.
It does so by providing a framework that traverses an XML tree, tests tag nodes, pulls and
manipulates data in a series of steps. It supports the combination of reusable and generalized
logical units of expressions and actions. Therefore there’s also a library with functions to deploy
and a module with contributed transformations.
Though inxs
should be usable for any problem that XSLT could solve, it is not modeled to
address XSLT users to get a quick grip on it. Anyone who enjoys XSLT should continue to do so.
So far the framework performs with acceptable speed with uses on text documents from the
humanities.
Let’s break its usage down with the second example from the README
:
1 2 3 4 5 6 | transformation = Transformation(
generate_skeleton,
Rule('person', extract_person),
lib.sort('persons', itemgetter(1)),
list_persons,
result_object='context.html', context={'persons': []})
|
A transformation is set up by instantiating a inxs.Transformation
(line 1) with a series
of transformation steps (lines 2-5) passed as positional argument s and two
configuration values (line 6) provided as keyword arguments.
The first step (line 2) is a function that creates a skeleton for the resulting HTML markup and
stores it in the context
namespace:
def generate_skeleton(context):
context.html = new_tag_node(
"html", namespace='http://www.w3.org/1999/xhtml',
children=(
tag("head",
tag("title", "Testing XML Example")),
tag("body", (
tag("h1", "Persons"),
tag("ul")
)),
)
)
When a transformation calls a handler function it does so by applying dependency injection as may
be known from pytest’s fixtures. The passed arguments are resolved from
inxs.Transformation._available_symbols
where any object that has previously been added to
the context
namespace is available as well as the context
itself.
Line 3 defines something that is used more often in real world uses than here. A inxs.Rule
that tests the transformation root and its descendants for defined properties. In the
example all nodes with a person
tag will be passed to the associated
handler function:
def extract_person(node: TagNode, persons):
persons.append(
(first(node.css_select("name")).full_text,
first(node.css_select("family-name")).full_text)
)
delb’s API is used to fetch child nodes of the matching nodes, extract their text and appends
them in a tuple to a list that was defined in the context
argument of the configuration values
(line 7).
Rules can also test anything outside the scope of a node, the utilized functions however aren’t
‘dependency injected’ to avoid overhead. They are called with node
and transformation
as
arguments and take it from there. See inxs.If()
for an example.
The last two steps (line 4 and 5) eventually sort (inxs.lib.sort()
with
operator.itemgetter()
) and append the data to the HTML tree that was prepared by the step in
line 2:
def list_persons(previous_result, html: TagNode):
first(html.css_select("html|body html|ul")).append_child(
*(html.new_tag_node("li", children=[f'{x[1]}, {x[0]}'])
for x in previous_result)
)
The argument previous_result
is resolved to the object that the previous function returned,
again the delb
API and Python’s f-string s are used to generate the result.
As the transformation was configured with context.html
as result object, the transformation
returns the object referenced as html
(see handler function in line 2) from the context. If the
transformation hasn’t explicitly configured a result object, (per default a copy of) the
transformation root is returned. Any other data is discarded.
The initialized transformation can now be called with a delb.Document
or
delb.TagNode
instance as transformation root:
>>> result = transformation(document) # doctest: +SKIP
A transformation root can be any node within a document, leaving siblings and ancestors
untouched. A transformation works on a copy of the document’s tree unless the configuration
contains a key copy
set to False
or the transformation is called with such keyword
argument.
Transformations can also be used as simple steps - then invoked with the
transformation root - or as rule handlers - then invoked with each matching node.
Per default these do not operate on copies, to do so inxs.lib.f()
can be employed:
# as a simple step
f(sub_transformation, 'root', copy=True)
# as a rule handler
f(sub_transformation, 'node', copy=True)
Any transformation step, condition or handler can be grouped into sequence s to encourage code recycling - But don’t take that as a permission to barbarously patching fragments of existing solutions together that you might feel are similar to your problem. It’s taken care that the items are retained as when a transformation was initialized if groups were mutable types.
Now that the authoritarian part is reached, be advised that using expressive and unambiguous names is essential when designing transformations and their components. As a rule of thumb, a simple transformation step should fit into one line, rules into two, maybe up to four. If it gets confusing to read, use variables, grouping (more reusability) or dedicated functions (more performance) - again, mind the names! Reciting the Zen of Python on a daily basis makes you a beautiful person. Yes, even more.
To get a grip on implementing own condition test functions and handler function s, it’s
advised to study the inxs.lib
module.
And now, space for some spots-on-.. sections.
Traversal strategies¶
When a rule is evaluated, the document (sub-)tree is traversed in a specified order. There are three aspects that must be combined to define that order and are available as constants that are to be or’ed bitwise:
inxs.TRAVERSE_DEPTH_FIRST
/inxs.TRAVERSE_WIDTH_FIRST
inxs.TRAVERSE_LEFT_TO_RIGHT
/inxs.TRAVERSE_RIGHT_TO_LEFT
inxs.TRAVERSE_TOP_TO_BOTTOM
/inxs.TRAVERSE_BOTTOM_TO_TOP
Rules can be initiated with such value as traversal_order
argument and override the
transformation’s one (that one defaults to …_DEPTH_FIRST | …_LEFT_TO_RIGHT | …_TOP_TO_BOTTOM
).
Not all strategies are are implemented yet.
inxs.TRAVERSE_ROOT_ONLY
sets a strategy that only considers the transformation root. It
is also set implicitly for rules that contain a '/'
as condition (see
Rule condition shortcuts).
Rule condition shortcuts¶
Strings can be used to specify certain rule conditions:
/
selects only the transformation root*
selects all nodes - should only be used if there are no other conditions- any string that contains
://
selects nodes with a namespace that matches the string - strings that contain only letters select nodes whose local name matches the string
- if a string can be translated to an XPath expression with cssselect and thus can be considered a valid css selector, the result is used like the following; mind that you can use namespace prefixes if you know the prefixes, otherwise this is not an option to match a node from a namespace that’s not the transformation root’s default
- all other strings will select all nodes that an XPath evaluation of that string on the transformation root returns
Another shortcut is to pass a dictionary to test an node’s attributes, see
inxs.MatchesAttributes()
for details.
Speaking of conditions, see inxs.Any()
, inxs.OneOf()
and inxs.Not()
to overcome
the logical and
evaluation of all tests.
Global configuration¶
inxs
caches and reuses evaluator and handler functions with identical arguments where possible.
By default these caches are not limited in size and they might eventually grow larger than the
memory that was saved in big, long-running applications that create a lot of short-living
transformations. To limit the size of each of these last-recently-used-caches, the environment
variable HANDLER_CACHES_SIZE
can be set. The value should be a power of two.
Caveats¶
Modifications during iteration¶
Similar to iteration over mutable types in Python, adding, moving or deleting nodes to the
tree breaks the iteration of a rule over nodes. Thus such modifications must be applied in a
simple transformation step; e.g. to remove all <br>
nodes from a document:
def collect_trash(node, trashbin):
trashbin.append(node)
transformation = Transformation(
Rule('br', collect_trash),
lib.remove_nodes('trashbin'),
context={'trashbin': []})
Debugging / Logging¶
There are functions in the inxs.lib
module to log information about a transformation’s state
at info level. There’s a logger
object in that module too that needs to be set up with a
handler and a log level in order to get the output (see logging
). inxs
itself produces
very noisy messages at debug level.
inxs.lib.debug_dump_document()
, inxs.lib.debug_message()
and
inxs.lib.debug_symbols()
can be used as handler function.
inxs.lib.dbg()
and inxs.lib.nfo()
can be used within test and handler functions.
Due to its rather sparse and dynamic design, the exception tracebacks that are produced aren’t very helpful as they contain no information about the context of an exception. To tackle one of those, a minimal non-working example is preferred to debug.
Glossary¶
- configuration
- The configuration of a transformation is a
types.SimpleNamespace
object that is bound as itsconfig
property and is populated by passing keywords arguments to its initialization. It is intended to be an immutable container for key-value-pairs that persist through transformation’s executions. Mind that it’s immutability isn’t completely enforced, manipulating it or its members might result in unexpected behaviour. It can be referred to in handler function’s signatures asconfig
, the same is true for its member unless overridden ininxs.Transformation._available_symbols
. Seeinxs.Transformation
for details on reserved names in the configuration namespace. - context
- The context of a transformation is a
types.SimpleNamespace
instance and intended to hold any mutable values during a transformation. It is initialized from the values stored in the configuration’scontext
value and the overriding keywords provided when calling ainxs.Transformation
instance. - handler function
- Handler functions can be employed as simple transformation steps
or as conditionally executed
handlers
of ainxs.Rule
. Any of their signature’s argument s must be available ininxs.Transformation._available_symbols
upon the time the function gets called. - transformation root
- This is the node that a transformation instance is called with. Any traverser will return neither its ancestors nor its siblings.
- transformation steps
- Transformation steps are handler functions or
inxs.Rule
s that define the actions taken when a transformation is processed. The steps are stored as a linear graph, rudimentary branching can be achieved by using rules that call other transformations.
API documentation¶
See also:
inxs.contrib module¶
This module contains transformations that are supposedly of common interest.
-
inxs.contrib.
reduce_whitespaces
= <inxs.Transformation object>¶ Normalizes any whitespace character in text nodes to a simple space and reduces consecutive ones to one. Leading or tailing whitespaces are not stripped away.
-
inxs.contrib.
remove_empty_nodes
= <inxs.Transformation object>¶ Removes nodes without attributes, text and children.
inxs.lib module¶
This module contains common functions that can be used for either Rule
s’ tests, as handler functions or simple transformation steps.
Community contributions are highly appreciated, but it’s hard to layout hard criteria for what belongs here and what not. In doubt open a pull request with your proposal as far as it proved functional to you, it doesn’t need to be polished at that point.
-
inxs.lib.
add_html_classes
[source]¶ Adds the string tokens passed as positional arguments to the
classes
attribute of a node specified bytarget
. An argument can also be a sequence of strings or aRef()
that yields on of the two. Per default that is aRef()
to the matching node of a rule.
-
inxs.lib.
append
[source]¶ Appends the object referenced by
symbol
(default: the result of the previous handler function) to the object available asname
in theTransformation._available_symbols
. If the object is adelb.TagNode
instance andcopy_node
isTrue
, a copy that includes all descendant nodes is appended to the target.
-
inxs.lib.
cleanup_namespaces
(root: delb.nodes.TagNode, previous_result: Any) → Any[source]¶ Cleanup the namespaces of the tree. This should always be used at the end of a transformation when nodes’ namespaces have been changed.
-
inxs.lib.
clear_attributes
(node: delb.nodes.TagNode, previous_result: Any) → Any[source]¶ Deletes all attributes of an node.
-
inxs.lib.
concatenate
[source]¶ Concatenate the given parts which may be lists or strings as well as callables returning such.
-
inxs.lib.
debug_dump_document
[source]¶ Dumps all contents of the node referenced by
name
from theinxs.Transformation._available_symbols
to the log at info level.
-
inxs.lib.
debug_symbols
[source]¶ Logs the representation strings of the objects referenced by
names
ininxs.Transformation._available_symbols
at info level.
-
inxs.lib.
f
(func, *args, **kwargs)[source]¶ Wraps the callable
func
which will be called asfunc(*args, **kwargs)
, the function and any argument can be given asinxs.Ref()
.
-
inxs.lib.
get_text
(node: delb.nodes.TagNode)[source]¶ Returns the content of the matched node’s descendants of
delb.TextNode
type.
-
inxs.lib.
get_variable
[source]¶ Gets the object referenced as
name
from the context. It is then available as symbolprevious_result
.
-
inxs.lib.
has_attributes
(node: delb.nodes.TagNode, _)[source]¶ Returns
True
if the node has attributes.
-
inxs.lib.
has_children
(node: delb.nodes.TagNode, _)[source]¶ Returns
True
if the node has descendants.
-
inxs.lib.
has_matching_text
[source]¶ Returns
True
if the text contained by the node and its descendants has a matches the providedpattern
.
-
inxs.lib.
has_text
(node: delb.nodes.TagNode, _)[source]¶ Returns
True
if the node has anydelb.TextNode
.
-
inxs.lib.
insert_fontawesome_icon
[source]¶ Inserts the html markup for an icon from the fontawesome set with the given
name
atposition
of which onlyafter
is implemented atm.It employs semantics for Font Awesome 5.
-
inxs.lib.
join_to_string
[source]¶ Joins the object referenced by
symbol
around the givenseparator
and returns it.
-
inxs.lib.
make_node
(**node_args)[source]¶ Creates a new tag node in the root node’s context, takes the arguments of
delb.TagNode.new_tag_node()
that must be provided as keyword arguments. The node is then available as symbolprevious_result
.
-
inxs.lib.
pop_attributes
[source]¶ Pops all attributes with name from
names
and returns a mapping with names and values. Whenignore_missing
isTrue
KeyError
exceptions pass silently.
-
inxs.lib.
put_variable
[source]¶ Puts
value``as ``name
to the context namespace, by default the value is determined by ainxs.Ref()
toprevious_result
.
-
inxs.lib.
remove_attributes
[source]¶ Removes all attributes with the keys provided as
names
from the node.
-
inxs.lib.
remove_namespace
(node: delb.nodes.TagNode, previous_result)[source]¶ Removes the namespace from the node. When used,
cleanup_namespaces()
should be applied at the end of the transformation.
-
inxs.lib.
remove_node
(node: delb.nodes.TagNode)[source]¶ A very simple handler that just removes a node and its descendants from a tree.
-
inxs.lib.
remove_nodes
[source]¶ Removes all nodes from their tree that are referenced in a list that is available as
references
. The nodes’ children are retained whenkeep_children
is passed asTrue
, or only the contained text whenpreserve_text
is passed asTrue
. The reference list is cleared afterwards ifclear_ref
isTrue
.
-
inxs.lib.
rename_attributes
(translation_map: Mapping[str, str]) → Callable[source]¶ Renames the attributes of a node according to the provided
translation_map
that consists of old name keys and new name values.
-
inxs.lib.
resolve_xpath_to_node
[source]¶ Resolves the objects from the context namespace (which are supposed to be XPath expressions) referenced by
names
with the one node that the expression matches orNone
. This is useful when a copied tree is processed and ‘XPath pointers’ are passed to the context when ainxs.Transformation
is called.
-
inxs.lib.
set_text
[source]¶ Sets the nodes’s first child node that is of
delb.TextNode
type to the one provided astext
, it can also be ainxs.Ref()
. If the first node isn’t a text node, one will be inserted.
-
inxs.lib.
sort
[source]¶ Sorts the object referenced by
name
in the context usingkey
as key function.
inxs.utils module¶
-
inxs.utils.
reduce_whitespaces
(text: str, translate_to_space: str = '\t\n\r\x0b\x0c', strip: str = 'lr') → str[source]¶ Reduces the whitespaces of the provided string by replacing any of the defined whitespaces with a simple space (U+20) and stripping consecutive ones to a single one.
Parameters: - text – The input string.
- translate_to_space – The characters that should are defined as whitespace. Defaults to all common whitespace characters from the ASCII set.
- strip – The ‘sides’ of the string to strip from any whitespace at all, indicated by ‘l’ for the beginning and/or ‘r’ for the end of the string.
Returns: The resulting string.
inxs
module contents¶
-
inxs.
logger
= <Logger inxs (WARNING)>¶ Module logger, configure as you need.
-
exception
inxs.
AbortRule
[source]¶ Bases:
inxs.FlowControl
Can be raised to abort the evaluation of all the currently processed
inxs.Rule
‘s remaining tests and handlers. No further nodes will be considered for that rule. This is similar to Python’s builtinbreak
in iterations.
-
exception
inxs.
AbortTransformation
[source]¶ Bases:
inxs.FlowControl
Can be raised to cancel the remaining transformation steps.
-
exception
inxs.
SkipToNextNode
[source]¶ Bases:
inxs.FlowControl
Can be raised to abort handling of the current node. This is similar to Python’s builtin
continue
in iterations.
-
inxs.
Any
(*conditions) → Callable[source]¶ Returns a callable that evaluates the provided test functions and returns
True
if any of them returned that.
-
inxs.
Not
(*conditions) → Callable[source]¶ Returns a callable that evaluates the provided test functions and returns
True
if any of them returnedFalse
.
-
inxs.
OneOf
(*conditions) → Callable[source]¶ Returns a callable that evaluates the provided test functions and returns
True
if exactly one of them returned that.
-
inxs.
MatchesAttributes
(constraints: Union[Dict[Union[str, Pattern[~AnyStr]], Union[str, Pattern[~AnyStr], None]], Callable]) → Callable[source]¶ Returns a callable that tests an node’s attributes for constrains defined in a mapping. All constraints must be matched to resolve as true. Expected keys and values can be provided as string or compiled regular expression object from the
re
module. ANone
as value constraint evaluates as true if the key is in the attributes regardless its value. It also implies that at least one attribute must match the key’s constraint if this one is a regular expression object. Alternatively a callable can be passed that returns such mappings during the transformation.
-
inxs.
MatchesXPath
[source]¶ Returns a callable that tests an node for the given XPath expression (whether the evaluation result on the transformation root contains it). If the
xpath
argument is a callable, it will be called with the current transformation as argument to obtain the expression.
-
inxs.
If
(x: Any, operator: Callable, y: Any) → Callable[source]¶ Returns a callable that can be used as condition test in a
Rule
. The argumentsx
andy
can be given as callables that will be used to get theoperator
’s input values during execution. Before you implement your own operators, mind that there are a lot available within Python’s__builtins__
and the standard library, in particular theoperator
module.Examples:
>>> If(Ref('previous_result'), operator.is_not, None) # doctest: +SKIP
-
inxs.
Ref
[source]¶ Returns a callable that can be used for value resolution in a condition test or handler function that supports such. The value will be looked up during the processing of a transformation in
Transformation._available_symbols
by the givenname
. This allows to reference dynamic values in transformation steps andRule
s.
-
class
inxs.
Rule
(conditions: Union[Callable, AnyStr, Dict[Union[str, Pattern[~AnyStr]], Union[str, Pattern[~AnyStr], None]], Sequence[Union[Callable, AnyStr, Dict[Union[str, Pattern[~AnyStr]], Union[str, Pattern[~AnyStr], None]]]]], handlers: Union[Callable, Sequence[Callable]], name: str = None, traversal_order: int = None)[source]¶ Bases:
object
Instances of this class can be used as conditional transformation steps that are evaluated against all traversed nodes.
Parameters: - conditions (A single callable, string or mapping, or a sequence
of such.) – All given conditions must evaluate as
True
in order for this rule to be applied. Strings and mappings can be provided as shortcuts, see Rule condition shortcuts for details. The condition test functions are always called with the currently evaluatednode
and theTransformation
instance as arguments. There are helper functions for grouping conditions logically:Any()
,Not()
andOneOf()
. - handlers (A single callable or a sequence of such.) – These handlers will be called if the conditions matched.
They can take any argument whose name is available in
Transformation._available_symbols
. - name (String.) – The optional rule’s name.
- traversal_order (Integer.) – An optional traversal order that overrides the
transformation’s default
Transformation.config.traversal_order
, see Traversal strategies for details.
- conditions (A single callable, string or mapping, or a sequence
of such.) – All given conditions must evaluate as
-
class
inxs.
Once
(*args, **kwargs)[source]¶ Bases:
inxs.Rule
This is a variant of
Rule
that is only applied on the first match.
-
class
inxs.
Transformation
(*steps, **config)[source]¶ Bases:
object
A transformation instance is defined by its transformation steps and configuration. It is to be called with a
delb.Document
ordelb.TagNode
instance as transformation root, only this node (or the root node of aDocument
) and its children will be considered during traversal.Parameters: - steps – The designated transformation steps of the instance are given as a sequence of positional arguments.
- config –
The configuration values for the instance are passed as keyword arguments. Beside the following keywords, it can be populated with any key-value-pairs that will be available in
inxs.Transformation._available_symbols
during a transformation. The defaults are defined inconfig_defaults
.context
can be provided as mapping with items that are added to the context before a (sub-)document is processed.common_rule_conditions
can be used to define one or more conditions that must match in all rule evaluations. E.g. a transformation could be restricted to nodes with a certain namespace without redundantly defining that per rule. Can be given as a single object (e.g. a string) or as sequence.copy
is a boolean that defaults toTrue
and indicates whether to process on a copy of the document’s tree object.name
can be used to identify a transformation.result_object
sets the transformation’s attribute that is returned as result. Dot-notation lookup (e.g.context.target
) is implemented. Per default the transformation root is returned.traversal_order
sets the default traversal order for rule evaluations and itself defaults to depth first, left to right, to to bottom. See Traversal strategies for possible values.
-
_available_symbols
¶ This mapping contains items that are used for the dependency injection of handler functions. These names are included:
- All attributes of the transformation’s configuration, overridden by the following.
- All attributes of the transformation’s context, overridden by the following.
config
- The configuration namespace object.context
- The context namespace object.node
- The node that matched aRule
’s conditions orNone
in case of simple transformation steps.previous_result
- The result that was returned by the previously evaluated handler function.root
- The root node of the processed (sub-)document a.k.a. transformation root.transformation
- The callingTransformation
instance.
-
config_defaults
= {'common_rule_conditions': None, 'context': {}, 'copy': True, 'name': None, 'result_object': 'root', 'traversal_order': 7}¶ The default configuration values. Changing members on an instance actually affects the class unless a copy of this mapping as copied and bound as instance attribute.
-
name
¶ The
name
member of the transformation’s configuration.
-
root
¶ This property can be used to access the root node of the currently processed (sub-)document.
General Index¶
Frequently Asked Questions¶
Why, oh, why?¶
TODO
Am I cognitively fit to use inxs
?¶
If you’re comfortable with Python and delb, you propably are. Just give it a try to solve a smaller problem. If you don’t get a grip on something that may be due to the immature documentation.
If you aren’t, you should be willing to get acquainted with both. In this case it is recommended
to test your understanding and assumptions without inxs
as well. bpython and Jupyter are
great playgrounds.
Can I get help?¶
In case you carefully studied the documentation, just open an issue on the issue tracker.
Mind that you can’t get supported to solve your actual problem, but rather to understand and use
inxs
as a tool to do so.
Can I produce HTML output with inxs
?¶
One thing you may do is to rather produce XHTML, but that is lacking modern HTML features you may want to use. Here’s a trick to produce actual HTML:
- produce an XML tree without namespace declarations using the HTML tag set
- serialize the result into a string
- mangle that through pytidylib
Contributing¶
Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given.
You can contribute in many ways:
Types of Contributions¶
Report Bugs¶
Report bugs at https://github.com/funkyfuture/inxs/issues.
If you are reporting a bug, please include:
- Your Python interpreter and inxs’ version.
- Any details about your local setup that might be helpful in troubleshooting.
- Detailed steps to reproduce the bug.
Fix Bugs¶
Look through the GitHub issues for bugs. Anything tagged with “bug” is open to whoever wants to fix it.
Implement Features¶
Look through the GitHub issues for features. Anything tagged with “enhancement” is open to whoever wants to implement it.
Write Documentation¶
inxs could always use more documentation, whether as part of the official inxs docs, in docstrings, or even on the web in blog posts, articles, and such.
Submit Feedback¶
The best way to send feedback is to file an issue at https://github.com/funkyfuture/inxs/issues.
If you are proposing a feature:
- Explain in detail how it would work.
- Keep the scope as narrow as possible, to make it easier to implement.
- Remember that this is a volunteer-driven project, and that contributions are welcome :)
Get Started!¶
Ready to contribute? Here’s how to set up inxs for local development.
Install the needed Prerequisites
Fork the inxs repo on GitHub.
Clone your fork locally:
$ git clone git@github.com:your_name_here/inxs.git
Install your local copy into a virtualenv. Assuming you have pew installed, this is how you set up your fork for local development:
$ cd inxs/ $ pew new -a $(pwd) inxs $ pip install -r requirements-dev.txt $ python setup.py develop
Create a branch for local development:
$ git checkout -b name-of-your-bugfix-or-feature
Now you can make your changes locally.
When you’re done making changes, format the code with black and check that your changes pass all QA tests:
$ make black $ tox
Commit your changes and push your branch to GitHub:
$ git add . $ git commit -m "Your detailed description of your changes." $ git push origin name-of-your-bugfix-or-feature
Submit a pull request through the GitHub website.
Pull Request Guidelines¶
Before you submit a pull request, check that it meets these guidelines:
- The pull request should include tests.
- If the pull request adds functionality, the docs should be updated.
- The pull request should work for Python 3.6. Check https://travis-ci.org/funkyfuture/inxs/pull_requests and make sure that the tests pass for all supported Python versions.
History¶
0.2b1 (2019-06-23)¶
- refactored to base on delb instead of lxml
- removed from the available symbols for handler functions:
tree
xpath_evaluator
(useroot.xpath
instead)
- renamed available symbols for handler functions:
element
->node
- renamed in core:
SkipToNextElement
->SkipToNextNode
- removed from the lib:
drop_siblings
extract_text
has_tail
init_elementmaker
merge
replace_text
sub
- renamed in the lib:
make_element
->make_node
remove_element
->remove_node
remove_elements
->remove_nodes
sorter
->sort
strip_attributes
->remove_attributes
strip_namespace
->remove_namespace
Various arguments to functions and methods have been renamed accordingly.
0.1b1 (2017-06-25)¶
- new: Allows the definition that any rule must match per transformation as
common_rule_conditions
. - Minor improvements and fixes.
0.1b0 (2017-06-19)¶
- First beta release.
0.1a0 (2017-05-02)¶
- First release on PyPI.
inxs – A Python framework for XML transformations without boilerplate.¶
inxs is inexcessive.
inxs is not XSLT.
inxs is ISC-licensed.
inxs is fully documented here: https://inxs.readthedocs.io/en/latest/
At a glimpse¶
Solving the Wikipedia XSLT example #1:
def extract_person(node: TagNode):
return node.attributes['username'], first(node.css_select("name")).full_text
def append_person(previous_result, result: TagNode):
result.append_child(result.new_tag_node(
"name", attributes={"username": previous_result[0]},
children=[previous_result[1]]
))
transformation = Transformation(
Rule('person', (extract_person, append_person)),
result_object='context.result', context={'result': new_tag_node('root')})
# that's four lines less LOC than the XSLT implementation
Solving the Wikipedia XSLT example #2:
def generate_skeleton(context):
context.html = new_tag_node(
"html", namespace='http://www.w3.org/1999/xhtml',
children=(
tag("head",
tag("title", "Testing XML Example")),
tag("body", (
tag("h1", "Persons"),
tag("ul")
)),
)
)
def extract_person(node: TagNode, persons):
persons.append(
(first(node.css_select("name")).full_text,
first(node.css_select("family-name")).full_text)
)
def list_persons(previous_result, html: TagNode):
first(html.css_select("html|body html|ul")).append_child(
*(html.new_tag_node("li", children=[f'{x[1]}, {x[0]}'])
for x in previous_result)
)
transformation = Transformation(
generate_skeleton,
Rule('person', extract_person),
lib.sort('persons', itemgetter(1)),
list_persons,
result_object='context.html', context={'persons': []})
# that's four lines more LOC than the XSLT implementation
Here you can find the source repository and issue tracker of inxs.