Skip to content

Commit

Permalink
Split requirements to extras cli, soap and lxml
Browse files Browse the repository at this point in the history
Closes #419
  • Loading branch information
tefra authored Mar 6, 2021
1 parent 711c62f commit 3c193be
Show file tree
Hide file tree
Showing 18 changed files with 287 additions and 147 deletions.
18 changes: 18 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,21 @@ jobs:
- name: Benchmark
run: |
tox -e benchmarks
minimum:
name: Minimum Installation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.7
- name: Install pip install .
run: |
pip install -e .
- name: Verify
run: |
python tests/integration/benchmarks/conftest.py -c XmlEventWriter
python tests/integration/benchmarks/conftest.py -c XmlEventHandler
python tests/integration/benchmarks/conftest.py -c JsonParser
python tests/integration/benchmarks/conftest.py -c JsonSerializer
xsdata | xargs -0 python -c "import sys; assert 'Install cli' in sys.argv[1]"
7 changes: 7 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ unknown properties and to process xinclude statements.
xsData is constantly tested against the
`W3C XML Schema 1.1 test suite <https://github.com/tefra/xsdata-w3c-tests>`_.

Getting started
---------------

.. code-block:: bash
$ pip install xsdata[cli,lxml,soap]
.. image:: https://github.com/tefra/xsdata/raw/master/docs/_static/demo.svg

Check the documentation `demos <https://xsdata.readthedocs.io/en/latest/demos.html>`_ or
Expand Down
8 changes: 8 additions & 0 deletions docs/codegen.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@
Command Line
============


Make sure the cli requirements are installed.

.. code-block:: bash
$ pip install xsdata[cli]
.. command-output:: xsdata --help


Expand Down
19 changes: 17 additions & 2 deletions docs/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,26 @@ Getting started
Install using pip
-----------------

The recommended method is to use a virtual environment
The recommended method is to use a virtual environment.

.. code-block:: bash
$ pip install xsdata
$ pip install xsdata[cli,lxml,soap]
.. hint::

- Install the cli requirements for the code generator
- Install the soap requirements for the builtin wsdl client
- Install lxml if you want to use one of the lxml handlers/writers instead of
the builtin python xml implementations.

xsdata has a monthly release cycle, in order to use the latest updates you can also
install directly from the git repo.

.. code-block:: bash
$ pip install git+https://github.com/tefra/xsdata@master#egg=xsdata[cli,lxml]
Install using conda
-------------------
Expand Down
6 changes: 6 additions & 0 deletions docs/wsdl.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ The code generator in addition to models derived from xml schemas will also gene
dataclasses for messages and simple classes to describe the unique operations.


Make sure you install both cli and soap requirements.

.. code-block:: console
$ pip install xsdata[cli,soap]
.. code-block:: console
$ xsdata --wsdl --package calculator http://www.dneonline.com/calculator.asmx?WSDL
Expand Down
24 changes: 18 additions & 6 deletions docs/xml.rst
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,18 @@ XmlHandlers read the xml source and push build events to create the target class
xsData ships with multiple handlers based on lxml and native python that vary in
performance and features.

>>> from xsdata.formats.dataclass.parsers.handlers import XmlEventHandler
...
>>> parser = XmlParser(handler=XmlEventHandler)
>>> order = parser.from_path(xml_path)
>>> order.bill_to.street
'8 Oak Avenue'
.. hint::

If you installed xsdata with lxml the default handler is set to
:class:`~xsdata.formats.dataclass.parsers.handlers.LxmlEventHandler` otherwise
:class:`~xsdata.formats.dataclass.parsers.handlers.XmlEventHandler` will be used.

>>> from xsdata.formats.dataclass.parsers.handlers import XmlEventHandler
...
>>> parser = XmlParser(handler=XmlEventHandler)
>>> order = parser.from_path(xml_path)
>>> order.bill_to.street
'8 Oak Avenue'

.. hint::

Expand Down Expand Up @@ -292,6 +298,12 @@ xsData ships with multiple writers based on lxml and native python that may vary
in performance in some cases. The output of all them is consistent with a few
exceptions when handling mixed content with ``pretty_print=True``.

.. hint::

If you installed xsdata with lxml the default writer is set to
:class:`~xsdata.formats.dataclass.serializers.writers.LxmlEventWriter` otherwise
:class:`~xsdata.formats.dataclass.serializers.writers.XmlEventWriter` will be used.

.. doctest::

>>> from xsdata.formats.dataclass.serializers.writers import XmlEventWriter
Expand Down
39 changes: 23 additions & 16 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -33,30 +33,22 @@ project_urls =
[options]
packages = xsdata
install_requires =
click
click-default-group
click_log
docformatter
jinja2
lxml
requests
toposort
dataclasses;python_version<"3.7"
python_requires = >=3.6
include_package_data = True

[options.entry_points]
console_scripts =
xsdata=xsdata.cli:cli
xsdata=xsdata.__main__:main

[options.extras_require]
dev =
codecov
pre-commit
pytest
pytest-benchmark
pytest-cov
tox
cli =
click
click-default-group
click_log
docformatter
jinja2
toposort
docs =
sphinx
sphinx-autobuild
Expand All @@ -65,6 +57,17 @@ docs =
sphinx-inline-tabs
sphinx-material
sphinxcontrib-programoutput
lxml =
lxml
soap =
requests
test =
codecov
pre-commit
pytest
pytest-benchmark
pytest-cov
tox

[flake8]
exclude = tests/*
Expand All @@ -76,3 +79,7 @@ max-line-length = 88

[tool:pytest]
addopts = --color=yes --benchmark-skip --benchmark-columns=min,max,mean,median

[coverage:run]
omit =
xsdata/__main__.py
63 changes: 40 additions & 23 deletions tests/integration/benchmarks/conftest.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import logging

from tests import xsdata_temp_dir
from tests.fixtures.books import BookForm
from tests.fixtures.books import Books
Expand All @@ -6,6 +8,7 @@
from xsdata.formats.dataclass.parsers import XmlParser
from xsdata.formats.dataclass.serializers import JsonSerializer
from xsdata.formats.dataclass.serializers import XmlSerializer
from xsdata.logger import logger
from xsdata.models.datatype import XmlDate

xsdata_temp_dir.mkdir(parents=True, exist_ok=True)
Expand Down Expand Up @@ -43,23 +46,23 @@ def make_books(how_many: int):
)


def parse(source, handler, *args):
def parse(source, handler):
parser = XmlParser(context=context, handler=handler)
parser.from_bytes(source, Books)


def parse_json(source, *args):
def parse_json(source):
parser = JsonParser(context=context)
parser.from_bytes(source, Books)


def write(size, obj, writer, *args):
def write(size, obj, writer):
with xsdata_temp_dir.joinpath(f"benchmark_{size}.xml").open("w") as f:
serializer = XmlSerializer(writer=writer, context=context)
serializer.write(f, obj)


def write_json(size, obj, *args):
def write_json(size, obj):
with xsdata_temp_dir.joinpath(f"benchmark_{size}.json").open("w") as f:
serializer = JsonSerializer(context=context)
serializer.write(f, obj)
Expand All @@ -72,33 +75,47 @@ def write_json(size, obj, *args):
from xsdata.formats.dataclass.parsers import handlers
from timeit import Timer

components = [
"LxmlEventHandler",
"LxmlSaxHandler",
"XmlEventHandler",
"XmlSaxHandler",
"LxmlEventWriter",
"XmlEventWriter",
"JsonParser",
"JsonSerializer",
]

parser = argparse.ArgumentParser()
parser.add_argument(
"--component", default="parser", choices=["serializer", "parser"]
)
parser.add_argument("--format", default="xml", choices=["xml", "json"])
parser.add_argument("--handler", default="XmlEventWriter")
parser.add_argument("--number", default=1000, type=int)
parser.add_argument("--repeat", default=10, type=int)
parser.add_argument("-c", "--component", choices=components, required=True)
parser.add_argument("-n", "--number", default=1000, type=int)
parser.add_argument("-r", "--repeat", default=10, type=int)
args = parser.parse_args()

if args.component == "serializer":
func = write if args.format == "xml" else write_json
writer = getattr(writers, args.handler)
if args.component in writers.__all__:
component = getattr(writers, args.component)
books = make_books(args.number)
t = Timer(lambda: func(args.number, books, writer))

elif args.component == "parser":
func = parse if args.format == "xml" else parse_json
handler = getattr(handlers, args.handler)
fixture = xsdata_temp_dir.joinpath(f"benchmark_{args.number}.{args.format}")
t = Timer(lambda: write(args.number, books, component))
elif args.component in handlers.__all__:
fixture = xsdata_temp_dir.joinpath(f"benchmark_{args.number}.xml")
if not fixture.exists():
write(args.number, make_books(args.number), writers.XmlEventWriter)

component = getattr(handlers, args.component)
t = Timer(lambda: parse(fixture.read_bytes(), component))
elif args.component == "JsonParser":
component = JsonParser
fixture = xsdata_temp_dir.joinpath(f"benchmark_{args.number}.json")
if not fixture.exists():
write_func = write if args.format == "xml" else write_json
write_func(args.number, make_books(args.number), writers.LxmlEventWriter)
write_json(args.number, make_books(args.number))

t = Timer(lambda: func(fixture.read_bytes(), handler))
t = Timer(lambda: parse_json(fixture.read_bytes()))
elif args.component == "JsonSerializer":
component = JsonSerializer
books = make_books(args.number)
t = Timer(lambda: write_json(args.number, books))

print(f"Benchmark {component.__name__} - n{args.number}/r{args.repeat}")
result = t.repeat(repeat=args.repeat, number=1)
print("avg {}".format(statistics.mean(result)))
print("med {}".format(statistics.median(result)))
Expand Down
3 changes: 3 additions & 0 deletions tests/integration/benchmarks/test_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
readers_list = list(readers.__all__)
writers_list = list(writers.__all__)

readers_list.remove("default_handler")
writers_list.remove("default_writer")

random.shuffle(readers_list)
random.shuffle(writers_list)

Expand Down
4 changes: 2 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ envlist = py36,py37,py38,py39,pypy3
skip_missing_interpreters = true

[testenv]
extras = dev
extras = test,cli,soap,lxml
commands = pytest --cov=./xsdata --cov-branch --doctest-glob="docs/*.rst"

[testenv:benchmarks]
commands = pytest --benchmark-only tests/integration/benchmarks

[testenv:docs]
basepython = python3.7
extras = docs
extras = docs,cli
changedir = docs
commands =
xsdata init-config examples/config.sample.xml
Expand Down
15 changes: 15 additions & 0 deletions xsdata/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import sys


def main():
try:
from xsdata.cli import cli

cli()
except ImportError:
print('Install cli requirements "pip install xsdata[cli]"')
sys.exit(1)


if __name__ == "__main__":
main()
20 changes: 18 additions & 2 deletions xsdata/formats/dataclass/parsers/handlers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
from xsdata.formats.dataclass.parsers.handlers.lxml import LxmlEventHandler
from xsdata.formats.dataclass.parsers.handlers.lxml import LxmlSaxHandler
from typing import Type

from xsdata.formats.dataclass.parsers.handlers.native import XmlEventHandler
from xsdata.formats.dataclass.parsers.handlers.native import XmlSaxHandler
from xsdata.formats.dataclass.parsers.mixins import XmlHandler

try:
from xsdata.formats.dataclass.parsers.handlers.lxml import LxmlEventHandler
from xsdata.formats.dataclass.parsers.handlers.lxml import LxmlSaxHandler

def default_handler() -> Type[XmlHandler]:
return LxmlEventHandler


except ImportError: # pragma: no cover

def default_handler() -> Type[XmlHandler]:
return XmlEventHandler


__all__ = [
"LxmlEventHandler",
"LxmlSaxHandler",
"XmlEventHandler",
"XmlSaxHandler",
"default_handler",
]
Loading

0 comments on commit 3c193be

Please sign in to comment.