From 5323e146dce708833f07fcff48e6bd4c5f19681d Mon Sep 17 00:00:00 2001 From: Mathias Behrle Date: Tue, 31 Jan 2017 12:26:10 +0100 Subject: [PATCH] Merging upstream version 0.24.0. --- .editorconfig | 19 - .gitignore | 20 - .travis.yml | 40 - CHANGES | 17 + Makefile | 30 - PKG-INFO | 8 +- README.rst | 6 +- docs/Makefile | 216 ---- docs/_templates/sidebar-intro.html | 18 - docs/conf.py | 299 ----- docs/index.rst | 131 --- docs/make.bat | 263 ----- examples/code39.py | 8 - examples/echo_services.py | 5 - examples/eu_vat_service.py | 7 - examples/km_to_miles.py | 16 - setup.py | 2 +- src/zeep.egg-info/PKG-INFO | 89 ++ src/zeep.egg-info/SOURCES.txt | 110 ++ src/zeep.egg-info/dependency_links.txt | 1 + src/zeep.egg-info/not-zip-safe | 1 + src/zeep.egg-info/pbr.json | 1 + src/zeep.egg-info/requires.txt | 23 + src/zeep.egg-info/top_level.txt | 1 + src/zeep/__init__.py | 2 +- src/zeep/helpers.py | 5 + src/zeep/wsdl/bindings/soap.py | 4 +- src/zeep/wsdl/messages/soap.py | 20 +- src/zeep/wsdl/wsdl.py | 7 +- src/zeep/xsd/builtins.py | 14 +- src/zeep/xsd/elements.py | 24 +- src/zeep/xsd/indicators.py | 30 +- src/zeep/xsd/schema.py | 11 +- src/zeep/xsd/types.py | 7 +- src/zeep/xsd/valueobjects.py | 24 +- tests/integration/test_http_post.py | 34 + tests/integration/test_http_post.wsdl | 74 ++ tests/test_client.py | 20 +- tests/test_client_factory.py | 35 + tests/test_helpers.py | 38 +- tests/test_main.py | 31 + tests/test_pprint.py | 34 + tests/test_transports.py | 29 + tests/test_wsa.py | 268 +++++ tests/test_wsdl_arrays.py | 360 ++++++ tests/test_wsdl_messages_document.py | 1271 ++++++++++++++++++++++ tests/test_wsdl_messages_http.py | 390 +++++++ tests/test_wsdl_messages_rpc.py | 434 ++++++++ tests/test_wsdl_soap.py | 110 ++ tests/test_wsse_username.py | 238 ++++ tests/test_wsse_utils.py | 25 + tests/test_xsd_any.py | 280 +++++ tests/test_xsd_attributes.py | 420 +++++++ tests/test_xsd_builtins.py | 7 + tests/test_xsd_choice.py | 979 +++++++++++++++++ tests/test_xsd_complex_types.py | 233 ++++ tests/test_xsd_extension.py | 597 ++++++++++ tests/test_xsd_integration.py | 51 +- tests/test_xsd_parse.py | 27 + tests/test_xsd_schemas.py | 656 +++++++++++ tests/test_xsd_signatures.py | 187 ++++ tests/test_xsd_simple_types.py | 193 ++++ tests/test_xsd_types.py | 80 ++ tests/test_xsd_union.py | 91 ++ tests/test_xsd_valueobjects.py | 403 +++++++ tests/wsdl_files/soap-enc.xsd | 535 +++++++++ tests/wsdl_files/soap_import_2.wsdl | 42 + tests/wsdl_files/soap_import_main.wsdl | 38 + tests/wsdl_files/soap_transport_err.wsdl | 138 +++ tests/wsdl_files/test_import_2.xsd | 60 + tests/wsdl_files/xmldsig-core-schema.xsd | 309 ++++++ tox.ini | 7 - 72 files changed, 9058 insertions(+), 1145 deletions(-) delete mode 100644 .editorconfig delete mode 100644 .gitignore delete mode 100644 .travis.yml delete mode 100644 Makefile delete mode 100644 docs/Makefile delete mode 100644 docs/_templates/sidebar-intro.html delete mode 100644 docs/conf.py delete mode 100644 docs/index.rst delete mode 100644 docs/make.bat delete mode 100644 examples/code39.py delete mode 100644 examples/echo_services.py delete mode 100644 examples/eu_vat_service.py delete mode 100644 examples/km_to_miles.py create mode 100644 src/zeep.egg-info/PKG-INFO create mode 100644 src/zeep.egg-info/SOURCES.txt create mode 100644 src/zeep.egg-info/dependency_links.txt create mode 100644 src/zeep.egg-info/not-zip-safe create mode 100644 src/zeep.egg-info/pbr.json create mode 100644 src/zeep.egg-info/requires.txt create mode 100644 src/zeep.egg-info/top_level.txt create mode 100644 tests/integration/test_http_post.py create mode 100644 tests/integration/test_http_post.wsdl create mode 100644 tests/test_client_factory.py create mode 100644 tests/test_main.py create mode 100644 tests/test_pprint.py create mode 100644 tests/test_transports.py create mode 100644 tests/test_wsa.py create mode 100644 tests/test_wsdl_arrays.py create mode 100644 tests/test_wsdl_messages_document.py create mode 100644 tests/test_wsdl_messages_http.py create mode 100644 tests/test_wsdl_messages_rpc.py create mode 100644 tests/test_wsdl_soap.py create mode 100644 tests/test_wsse_username.py create mode 100644 tests/test_wsse_utils.py create mode 100644 tests/test_xsd_any.py create mode 100644 tests/test_xsd_attributes.py create mode 100644 tests/test_xsd_choice.py create mode 100644 tests/test_xsd_complex_types.py create mode 100644 tests/test_xsd_extension.py create mode 100644 tests/test_xsd_schemas.py create mode 100644 tests/test_xsd_signatures.py create mode 100644 tests/test_xsd_simple_types.py create mode 100644 tests/test_xsd_types.py create mode 100644 tests/test_xsd_union.py create mode 100644 tests/test_xsd_valueobjects.py create mode 100644 tests/wsdl_files/soap-enc.xsd create mode 100644 tests/wsdl_files/soap_import_2.wsdl create mode 100644 tests/wsdl_files/soap_import_main.wsdl create mode 100644 tests/wsdl_files/soap_transport_err.wsdl create mode 100644 tests/wsdl_files/test_import_2.xsd create mode 100644 tests/wsdl_files/xmldsig-core-schema.xsd delete mode 100644 tox.ini diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 4926c2f..0000000 --- a/.editorconfig +++ /dev/null @@ -1,19 +0,0 @@ -root = true - -[*.py] -line_length = 79 -multi_line_output = 4 -balanced_wrapping = true -known_first_party = zeep,tests -use_parentheses = true -indent_style = space -indent_size = 4 -tab_width = 4 - -[*.yml] -indent_size = 2 -shift_width = 2 - -[Makefile] -indent_style = tab -indent_size = 4 diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 166db7a..0000000 --- a/.gitignore +++ /dev/null @@ -1,20 +0,0 @@ -*.egg-info -*.pyc -.tox -.coverage -.eggs -.cache -.python-version -.venv -.idea/ -/build/ -/dist/ -/test_clients/ -/docs/_build/ -/frutsels/ -/server/ -/htmlcov/ - - -# Editors -.idea/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index d2de223..0000000 --- a/.travis.yml +++ /dev/null @@ -1,40 +0,0 @@ ---- -sudo: false -language: python - -python: - - '2.7' - - '3.3' - - '3.4' - - '3.5' - - 'pypy' - -install: - - | - if [ "$TRAVIS_PYTHON_VERSION" = "pypy" ]; then - export PYENV_ROOT="$HOME/.pyenv" - if [ -f "$PYENV_ROOT/bin/pyenv" ]; then - pushd "$PYENV_ROOT" && git pull && popd - else - rm -rf "$PYENV_ROOT" && git clone --depth 1 https://github.com/yyuu/pyenv.git "$PYENV_ROOT" - fi - export PYPY_VERSION="5.4" - "$PYENV_ROOT/bin/pyenv" install --skip-existing "pypy-$PYPY_VERSION" - virtualenv --python="$PYENV_ROOT/versions/pypy-$PYPY_VERSION/bin/python" "$HOME/virtualenvs/pypy-$PYPY_VERSION" - source "$HOME/virtualenvs/pypy-$PYPY_VERSION/bin/activate" - fi - - pip install codecov - - pip install -e .[test] - -script: - - py.test --cov=zeep --cov-report=term-missing - -after_success: - - codecov - -before_cache: - - rm -rf $HOME/.cache/pip/log - -cache: - directories: - - $HOME/.cache/pip diff --git a/CHANGES b/CHANGES index 468f461..1b6c737 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,20 @@ +0.24.0 (2016-12-16) +------------------- + - Don't fail the parsing of responses if an xsi:type references an non-existing + type. Instead log a message (#273) + - Fix serializing etree.Element instances in the helpers.serialize function + (#255) + - Fix infinite loop during parsing of xsd.Sequence where max_occurs is + unbounded (#256) + - Make the xsd.Element name kwarg required + - Improve handling of the xsd:anyType element when passed instances of + complexType's (#252) + - Silently ignore unsupported binding transports instead of an hard error + (#277) + - Support microseconds for xsd.dateTime and xsd.Time (#280) + - Don't mutate passed values to the zeep operations (#280) + + 0.23.0 (2016-11-24) ------------------- - Add Client.set_default_soapheaders() to set soapheaders which are to be used diff --git a/Makefile b/Makefile deleted file mode 100644 index 3244331..0000000 --- a/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -.PHONY: install clean test retest coverage docs - -install: - pip install -e .[docs,test] - pip install bumpversion twine wheel - -lint: - flake8 src/ tests/ - isort --recursive --check-only --diff src tests - -clean: - find . -name '*.pyc' -delete - -test: - py.test -vvv - -retest: - py.test -vvv --lf - -coverage: - py.test --cov=zeep --cov-report=term-missing --cov-report=html - -docs: - $(MAKE) -C docs html - -release: - pip install twine wheel - rm -rf dist/* - python setup.py sdist bdist_wheel - twine upload -s dist/* diff --git a/PKG-INFO b/PKG-INFO index 0810ebd..fc65fe8 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: zeep -Version: 0.23.0 +Version: 0.24.0 Summary: A modern/fast Python SOAP client based on lxml / requests Home-page: http://docs.python-zeep.org Author: Michael van Tellingen @@ -69,8 +69,10 @@ Description: ======================== If you encounter bugs then please `let me know`_ . A copy of the WSDL file if possible would be most helpful. - I'm also able to offer commercial support. Please contact me at - info@mvantellingen.nl for more information. + I'm also able to offer commercial support. As in contracting work. Please + contact me at info@mvantellingen.nl for more information. If you just have a + random question and don't intent to actually pay me for my support then please + DO NOT email me at that e-mail address but just use stackoverflow or something.. .. _let me know: https://github.com/mvantellingen/python-zeep/issues diff --git a/README.rst b/README.rst index 81d782f..e46a8b6 100644 --- a/README.rst +++ b/README.rst @@ -84,7 +84,9 @@ Support If you encounter bugs then please `let me know`_ . A copy of the WSDL file if possible would be most helpful. -I'm also able to offer commercial support. Please contact me at -info@mvantellingen.nl for more information. +I'm also able to offer commercial support. As in contracting work. Please +contact me at info@mvantellingen.nl for more information. If you just have a +random question and don't intent to actually pay me for my support then please +DO NOT email me at that e-mail address but just use stackoverflow or something.. .. _let me know: https://github.com/mvantellingen/python-zeep/issues diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index 49c4227..0000000 --- a/docs/Makefile +++ /dev/null @@ -1,216 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = _build - -# User-friendly check for sphinx-build -ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) -$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) -endif - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " applehelp to make an Apple Help Book" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " xml to make Docutils-native XML files" - @echo " pseudoxml to make pseudoxml-XML files for display purposes" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - @echo " coverage to run coverage check of the documentation (if enabled)" - -.PHONY: clean -clean: - rm -rf $(BUILDDIR)/* - -.PHONY: html -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -.PHONY: dirhtml -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -.PHONY: singlehtml -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -.PHONY: pickle -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -.PHONY: json -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -.PHONY: htmlhelp -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -.PHONY: qthelp -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Zeep.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Zeep.qhc" - -.PHONY: applehelp -applehelp: - $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp - @echo - @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." - @echo "N.B. You won't be able to view it unless you put it in" \ - "~/Library/Documentation/Help or install it in your application" \ - "bundle." - -.PHONY: devhelp -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/Zeep" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Zeep" - @echo "# devhelp" - -.PHONY: epub -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -.PHONY: latex -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -.PHONY: latexpdf -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -.PHONY: latexpdfja -latexpdfja: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through platex and dvipdfmx..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -.PHONY: text -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -.PHONY: man -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -.PHONY: texinfo -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -.PHONY: info -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -.PHONY: gettext -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -.PHONY: changes -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -.PHONY: linkcheck -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -.PHONY: doctest -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." - -.PHONY: coverage -coverage: - $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage - @echo "Testing of coverage in the sources finished, look at the " \ - "results in $(BUILDDIR)/coverage/python.txt." - -.PHONY: xml -xml: - $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml - @echo - @echo "Build finished. The XML files are in $(BUILDDIR)/xml." - -.PHONY: pseudoxml -pseudoxml: - $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml - @echo - @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/docs/_templates/sidebar-intro.html b/docs/_templates/sidebar-intro.html deleted file mode 100644 index af3fb9e..0000000 --- a/docs/_templates/sidebar-intro.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - -

Zeep is a modern SOAP client for Python

-

- -

- -

Links

- - diff --git a/docs/conf.py b/docs/conf.py deleted file mode 100644 index b233225..0000000 --- a/docs/conf.py +++ /dev/null @@ -1,299 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Zeep documentation build configuration file, created by -# sphinx-quickstart on Fri Mar 4 16:51:06 2016. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import sys -import os -import pkg_resources - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = ['sphinx.ext.autodoc'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# source_suffix = ['.rst', '.md'] -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'Zeep' -copyright = u'2016, Michael van Tellingen' -author = u'Michael van Tellingen' - - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '0.23.0' -release = version - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = ['_build'] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - -# If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False - -# If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = False - - -autodoc_default_flags = [':members:'] - - -# -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = 'alabaster' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -html_theme_options = { - 'github_user': 'mvantellingen', - 'github_banner': True, - 'github_repo': 'python-zeep', - 'travis_button': True, - 'codecov_button': True, - 'analytics_id': 'UA-75907833-1', -} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (relative to this directory) to use as a favicon of -# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -#html_extra_path = [] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -html_sidebars = { - '*': [ - 'sidebar-intro.html', 'globaltoc.html', 'sourcelink.html', - 'searchbox.html' - ] -} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None - -# Language to be used for generating the HTML full-text search index. -# Sphinx supports the following languages: -# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' -# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' -#html_search_language = 'en' - -# A dictionary with options for the search language support, empty by default. -# Now only 'ja' uses this config value -#html_search_options = {'type': 'default'} - -# The name of a javascript file (relative to the configuration directory) that -# implements a search results scorer. If empty, the default will be used. -#html_search_scorer = 'scorer.js' - -# Output file base name for HTML help builder. -htmlhelp_basename = 'Zeepdoc' - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', - -# Latex figure (float) alignment -#'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, 'Zeep.tex', u'Zeep Documentation', - u'Michael van Tellingen', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'zeep', u'Zeep Documentation', - [author], 1) -] - -# If true, show URL addresses after external links. -#man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - (master_doc, 'Zeep', u'Zeep Documentation', - author, 'Zeep', 'One line description of project.', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -#texinfo_appendices = [] - -# If false, no module index is generated. -#texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' - -# If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index 2aeb016..0000000 --- a/docs/index.rst +++ /dev/null @@ -1,131 +0,0 @@ -======================== -Zeep: Python SOAP client -======================== - -A fast and modern Python SOAP client - -Highlights: - * Modern codebase compatible with Python 2.7, 3.3, 3.4, 3.5 and PyPy - * Build on top of lxml and requests - * Supports recursive WSDL and XSD documents. - * Supports the xsd:choice and xsd:any elements. - * Uses the defusedxml module for handling potential XML security issues - * Support for WSSE (UsernameToken only for now) - * Experimental support for HTTP bindings - * Experimental support for WS-Addressing headers - * Experimental support for asyncio via aiohttp (Python 3.5+) - -Features still in development include: - * WSSE x.509 support (BinarySecurityToken) - * WS Policy support - - -A simple example: - -.. code-block:: python - - from zeep import Client - - client = Client('http://www.webservicex.net/ConvertSpeed.asmx?WSDL') - result = client.service.ConvertSpeed( - 100, 'kilometersPerhour', 'milesPerhour') - - assert result == 62.137 - - -Quick Introduction -================== - -Zeep inspects the wsdl document and generates the corresponding bindings. This -provides an easy to use programmatic interface to a soap server. - -The emphasis is on Soap 1.1 and Soap 1.2, however Zeep also offers experimental -support for HTTP Get and Post bindings. - -Parsing the XML documents is done by using the lxml library. This is the most -performant and compliant Python XML library currently available. This results -in major speed benefits when retrieving large soap responses. - -The SOAP specifications are unfortunately really vague and leave a lot of -things open for interpretation. Due to this there are a lot of WSDL documents -available which are invalid or SOAP servers which contain bugs. Zeep tries to -be as compatible as possible but there might be cases where you run into -problems. Don't hesitate to submit an issue in this case (please see -:ref:`reporting_bugs`). - - -Getting started -=============== - -You can install the latest version of zeep using pip:: - - pip install zeep - -The first thing you generally want to do is inspect the wsdl file you need to -implement. This can be done with:: - - python -mzeep - - -See ``python -mzeep --help`` for more information about this command. - - -.. note:: Since this module hasn't reached 1.0.0 yet their might be minor - releases which introduce backwards compatible changes. While I try - to keep this to a minimum it can still happen. So as always pin the - version of zeep you used (e.g. ``zeep==0.14.0``'). - - - -A simple use-case ------------------ - -To give you an idea how zeep works a basic example. - -.. code-block:: python - - import zeep - - wsdl = 'http://www.soapclient.com/xml/soapresponder.wsdl' - client = zeep.Client(wsdl=wsdl) - print(client.service.Method1('Zeep', 'is cool')) - -The WSDL used above only defines one simple function (``Method1``) which is -made available by zeep via ``client.service.Method1``. It takes two arguments -and returns a string. To get an overview of the services available on the -endpoint you can run the following command in your terminal. - -.. code-block:: bash - - python -mzeep http://www.soapclient.com/xml/soapresponder.wsdl - - -More information -================ - -.. toctree:: - :maxdepth: 2 - :name: mastertoc - - in_depth - datastructures - transport - wsa - wsse - plugins - helpers - reporting_bugs - changes - - -Support -======= - -If you encounter bugs then please `let me know`_ . Please see :doc:`reporting_bugs` -for information how to best report them. - -I'm also able to offer commercial support. Please contact me at -info@mvantellingen.nl for more information. - - -.. _let me know: https://github.com/mvantellingen/python-zeep/issues diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index bb121e7..0000000 --- a/docs/make.bat +++ /dev/null @@ -1,263 +0,0 @@ -@ECHO OFF - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set BUILDDIR=_build -set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . -set I18NSPHINXOPTS=%SPHINXOPTS% . -if NOT "%PAPER%" == "" ( - set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% - set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% -) - -if "%1" == "" goto help - -if "%1" == "help" ( - :help - echo.Please use `make ^` where ^ is one of - echo. html to make standalone HTML files - echo. dirhtml to make HTML files named index.html in directories - echo. singlehtml to make a single large HTML file - echo. pickle to make pickle files - echo. json to make JSON files - echo. htmlhelp to make HTML files and a HTML help project - echo. qthelp to make HTML files and a qthelp project - echo. devhelp to make HTML files and a Devhelp project - echo. epub to make an epub - echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter - echo. text to make text files - echo. man to make manual pages - echo. texinfo to make Texinfo files - echo. gettext to make PO message catalogs - echo. changes to make an overview over all changed/added/deprecated items - echo. xml to make Docutils-native XML files - echo. pseudoxml to make pseudoxml-XML files for display purposes - echo. linkcheck to check all external links for integrity - echo. doctest to run all doctests embedded in the documentation if enabled - echo. coverage to run coverage check of the documentation if enabled - goto end -) - -if "%1" == "clean" ( - for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i - del /q /s %BUILDDIR%\* - goto end -) - - -REM Check if sphinx-build is available and fallback to Python version if any -%SPHINXBUILD% 1>NUL 2>NUL -if errorlevel 9009 goto sphinx_python -goto sphinx_ok - -:sphinx_python - -set SPHINXBUILD=python -m sphinx.__init__ -%SPHINXBUILD% 2> nul -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ - exit /b 1 -) - -:sphinx_ok - - -if "%1" == "html" ( - %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/html. - goto end -) - -if "%1" == "dirhtml" ( - %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. - goto end -) - -if "%1" == "singlehtml" ( - %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. - goto end -) - -if "%1" == "pickle" ( - %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the pickle files. - goto end -) - -if "%1" == "json" ( - %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the JSON files. - goto end -) - -if "%1" == "htmlhelp" ( - %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run HTML Help Workshop with the ^ -.hhp project file in %BUILDDIR%/htmlhelp. - goto end -) - -if "%1" == "qthelp" ( - %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run "qcollectiongenerator" with the ^ -.qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Zeep.qhcp - echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Zeep.ghc - goto end -) - -if "%1" == "devhelp" ( - %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. - goto end -) - -if "%1" == "epub" ( - %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The epub file is in %BUILDDIR%/epub. - goto end -) - -if "%1" == "latex" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "latexpdf" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - cd %BUILDDIR%/latex - make all-pdf - cd %~dp0 - echo. - echo.Build finished; the PDF files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "latexpdfja" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - cd %BUILDDIR%/latex - make all-pdf-ja - cd %~dp0 - echo. - echo.Build finished; the PDF files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "text" ( - %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The text files are in %BUILDDIR%/text. - goto end -) - -if "%1" == "man" ( - %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The manual pages are in %BUILDDIR%/man. - goto end -) - -if "%1" == "texinfo" ( - %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. - goto end -) - -if "%1" == "gettext" ( - %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The message catalogs are in %BUILDDIR%/locale. - goto end -) - -if "%1" == "changes" ( - %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes - if errorlevel 1 exit /b 1 - echo. - echo.The overview file is in %BUILDDIR%/changes. - goto end -) - -if "%1" == "linkcheck" ( - %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck - if errorlevel 1 exit /b 1 - echo. - echo.Link check complete; look for any errors in the above output ^ -or in %BUILDDIR%/linkcheck/output.txt. - goto end -) - -if "%1" == "doctest" ( - %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest - if errorlevel 1 exit /b 1 - echo. - echo.Testing of doctests in the sources finished, look at the ^ -results in %BUILDDIR%/doctest/output.txt. - goto end -) - -if "%1" == "coverage" ( - %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage - if errorlevel 1 exit /b 1 - echo. - echo.Testing of coverage in the sources finished, look at the ^ -results in %BUILDDIR%/coverage/python.txt. - goto end -) - -if "%1" == "xml" ( - %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The XML files are in %BUILDDIR%/xml. - goto end -) - -if "%1" == "pseudoxml" ( - %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. - goto end -) - -:end diff --git a/examples/code39.py b/examples/code39.py deleted file mode 100644 index eda664f..0000000 --- a/examples/code39.py +++ /dev/null @@ -1,8 +0,0 @@ -from __future__ import print_function -import zeep - - -client = zeep.Client( - wsdl='http://www.webservicex.net/barcode.asmx?WSDL') -response = client.service.Code39('1234', 20, ShowCodeString=True, Title='ZEEP') -print(repr(response)) diff --git a/examples/echo_services.py b/examples/echo_services.py deleted file mode 100644 index 5270f95..0000000 --- a/examples/echo_services.py +++ /dev/null @@ -1,5 +0,0 @@ -from zeep.client import Client - -# RPC style soap service -client = Client('http://www.soapclient.com/xml/soapresponder.wsdl') -print(client.service.Method1('zeep', 'soap')) diff --git a/examples/eu_vat_service.py b/examples/eu_vat_service.py deleted file mode 100644 index e5ba15e..0000000 --- a/examples/eu_vat_service.py +++ /dev/null @@ -1,7 +0,0 @@ -from __future__ import print_function -import zeep - - -client = zeep.Client( - wsdl='http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl') -print(client.service.checkVat('NL', '170944128B01')) diff --git a/examples/km_to_miles.py b/examples/km_to_miles.py deleted file mode 100644 index d8821df..0000000 --- a/examples/km_to_miles.py +++ /dev/null @@ -1,16 +0,0 @@ -from __future__ import print_function -import zeep - - -client = zeep.Client( - wsdl='http://www.webservicex.net/ConvertSpeed.asmx?WSDL') - -client.wsdl.dump() - -print (client.service.ConvertSpeed(100, 'kilometersPerhour', 'milesPerhour')) - -http_get = client.bind('ConvertSpeeds', 'ConvertSpeedsHttpGet') -http_post = client.bind('ConvertSpeeds', 'ConvertSpeedsHttpPost') - -print(http_get.ConvertSpeed(100, 'kilometersPerhour', 'milesPerhour')) -print(http_post.ConvertSpeed(100, 'kilometersPerhour', 'milesPerhour')) diff --git a/setup.py b/setup.py index 8ba07bf..ea27113 100755 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ with open('README.rst') as fh: setup( name='zeep', - version='0.23.0', + version='0.24.0', description='A modern/fast Python SOAP client based on lxml / requests', long_description=long_description, author="Michael van Tellingen", diff --git a/src/zeep.egg-info/PKG-INFO b/src/zeep.egg-info/PKG-INFO new file mode 100644 index 0000000..fc65fe8 --- /dev/null +++ b/src/zeep.egg-info/PKG-INFO @@ -0,0 +1,89 @@ +Metadata-Version: 1.1 +Name: zeep +Version: 0.24.0 +Summary: A modern/fast Python SOAP client based on lxml / requests +Home-page: http://docs.python-zeep.org +Author: Michael van Tellingen +Author-email: michaelvantellingen@gmail.com +License: MIT +Description: ======================== + Zeep: Python SOAP client + ======================== + + A fast and modern Python SOAP client + + | Website: http://docs.python-zeep.org/ + | IRC: #python-zeep on Freenode + + Highlights: + * Modern codebase compatible with Python 2.7, 3.3, 3.4, 3.5 and PyPy + * Build on top of lxml and requests + * Supports recursive WSDL and XSD documents. + * Supports the xsd:choice and xsd:any elements. + * Uses the defusedxml module for handling potential XML security issues + * Support for WSSE (UsernameToken only for now) + * Experimental support for HTTP bindings + * Experimental support for WS-Addressing headers + * Experimental support for asyncio via aiohttp (Python 3.5+) + + Features still in development include: + * WSSE x.509 support (BinarySecurityToken) + * WS Policy support + + Please see for more information the documentation at + http://docs.python-zeep.org/ + + + + + Installation + ------------ + + .. code-block:: bash + + pip install zeep + + + Usage + ----- + .. code-block:: python + + from zeep import Client + + client = Client('tests/wsdl_files/example.rst') + client.service.ping() + + + To quickly inspect a WSDL file use:: + + python -mzeep + + + Please see the documentation at http://docs.python-zeep.org for more + information. + + + Support + ======= + + If you encounter bugs then please `let me know`_ . A copy of the WSDL file if + possible would be most helpful. + + I'm also able to offer commercial support. As in contracting work. Please + contact me at info@mvantellingen.nl for more information. If you just have a + random question and don't intent to actually pay me for my support then please + DO NOT email me at that e-mail address but just use stackoverflow or something.. + + .. _let me know: https://github.com/mvantellingen/python-zeep/issues + +Platform: UNKNOWN +Classifier: Development Status :: 4 - Beta +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy diff --git a/src/zeep.egg-info/SOURCES.txt b/src/zeep.egg-info/SOURCES.txt new file mode 100644 index 0000000..6f425bb --- /dev/null +++ b/src/zeep.egg-info/SOURCES.txt @@ -0,0 +1,110 @@ +CHANGES +LICENSE +README.rst +setup.cfg +setup.py +src/zeep/__init__.py +src/zeep/__main__.py +src/zeep/cache.py +src/zeep/client.py +src/zeep/exceptions.py +src/zeep/helpers.py +src/zeep/parser.py +src/zeep/plugins.py +src/zeep/transports.py +src/zeep/utils.py +src/zeep/wsa.py +src/zeep.egg-info/PKG-INFO +src/zeep.egg-info/SOURCES.txt +src/zeep.egg-info/dependency_links.txt +src/zeep.egg-info/not-zip-safe +src/zeep.egg-info/pbr.json +src/zeep.egg-info/requires.txt +src/zeep.egg-info/top_level.txt +src/zeep/asyncio/__init__.py +src/zeep/asyncio/bindings.py +src/zeep/asyncio/transport.py +src/zeep/wsdl/__init__.py +src/zeep/wsdl/definitions.py +src/zeep/wsdl/parse.py +src/zeep/wsdl/utils.py +src/zeep/wsdl/wsdl.py +src/zeep/wsdl/bindings/__init__.py +src/zeep/wsdl/bindings/http.py +src/zeep/wsdl/bindings/soap.py +src/zeep/wsdl/messages/__init__.py +src/zeep/wsdl/messages/base.py +src/zeep/wsdl/messages/http.py +src/zeep/wsdl/messages/mime.py +src/zeep/wsdl/messages/soap.py +src/zeep/wsse/__init__.py +src/zeep/wsse/username.py +src/zeep/wsse/utils.py +src/zeep/xsd/__init__.py +src/zeep/xsd/builtins.py +src/zeep/xsd/const.py +src/zeep/xsd/context.py +src/zeep/xsd/elements.py +src/zeep/xsd/indicators.py +src/zeep/xsd/parser.py +src/zeep/xsd/printer.py +src/zeep/xsd/schema.py +src/zeep/xsd/types.py +src/zeep/xsd/utils.py +src/zeep/xsd/valueobjects.py +src/zeep/xsd/visitor.py +tests/__init__.py +tests/conftest.py +tests/test_cache.py +tests/test_client.py +tests/test_client_factory.py +tests/test_helpers.py +tests/test_main.py +tests/test_pprint.py +tests/test_response.py +tests/test_transports.py +tests/test_wsa.py +tests/test_wsdl.py +tests/test_wsdl_arrays.py +tests/test_wsdl_messages_document.py +tests/test_wsdl_messages_http.py +tests/test_wsdl_messages_rpc.py +tests/test_wsdl_soap.py +tests/test_wsse_username.py +tests/test_wsse_utils.py +tests/test_xsd.py +tests/test_xsd_any.py +tests/test_xsd_attributes.py +tests/test_xsd_builtins.py +tests/test_xsd_choice.py +tests/test_xsd_complex_types.py +tests/test_xsd_extension.py +tests/test_xsd_integration.py +tests/test_xsd_parse.py +tests/test_xsd_schemas.py +tests/test_xsd_signatures.py +tests/test_xsd_simple_types.py +tests/test_xsd_types.py +tests/test_xsd_union.py +tests/test_xsd_valueobjects.py +tests/test_xsd_visitor.py +tests/utils.py +tests/integration/hello_world_recursive.wsdl +tests/integration/hello_world_recursive_import.wsdl +tests/integration/recursive_schema_a.xsd +tests/integration/recursive_schema_b.xsd +tests/integration/recursive_schema_c.xsd +tests/integration/recursive_schema_main.wsdl +tests/integration/test_hello_world_recursive.py +tests/integration/test_http_post.py +tests/integration/test_http_post.wsdl +tests/integration/test_recursive_schema.py +tests/wsdl_files/http.wsdl +tests/wsdl_files/soap-enc.xsd +tests/wsdl_files/soap.wsdl +tests/wsdl_files/soap_header.wsdl +tests/wsdl_files/soap_import_2.wsdl +tests/wsdl_files/soap_import_main.wsdl +tests/wsdl_files/soap_transport_err.wsdl +tests/wsdl_files/test_import_2.xsd +tests/wsdl_files/xmldsig-core-schema.xsd \ No newline at end of file diff --git a/src/zeep.egg-info/dependency_links.txt b/src/zeep.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/zeep.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/src/zeep.egg-info/not-zip-safe b/src/zeep.egg-info/not-zip-safe new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/zeep.egg-info/not-zip-safe @@ -0,0 +1 @@ + diff --git a/src/zeep.egg-info/pbr.json b/src/zeep.egg-info/pbr.json new file mode 100644 index 0000000..84a528f --- /dev/null +++ b/src/zeep.egg-info/pbr.json @@ -0,0 +1 @@ +{"is_release": false, "git_version": "70e9199"} \ No newline at end of file diff --git a/src/zeep.egg-info/requires.txt b/src/zeep.egg-info/requires.txt new file mode 100644 index 0000000..b63991d --- /dev/null +++ b/src/zeep.egg-info/requires.txt @@ -0,0 +1,23 @@ +appdirs>=1.4.0 +cached-property>=1.0.0 +defusedxml>=0.4.1 +isodate>=0.5.4 +lxml>=3.0.0 +requests>=2.7.0 +six>=1.9.0 +pytz + +[docs] +sphinx>=1.4.0 + +[test] +freezegun==0.3.7 +mock==2.0.0 +pretend==1.0.8 +pytest-cov==2.3.1 +pytest==3.0.2 +requests_mock>=0.7.0 +isort==4.2.5 +flake8==3.0.3 +flake8-blind-except==0.1.1 +flake8-debugger==1.4.0 diff --git a/src/zeep.egg-info/top_level.txt b/src/zeep.egg-info/top_level.txt new file mode 100644 index 0000000..6c16f20 --- /dev/null +++ b/src/zeep.egg-info/top_level.txt @@ -0,0 +1 @@ +zeep diff --git a/src/zeep/__init__.py b/src/zeep/__init__.py index 606d054..22d0183 100644 --- a/src/zeep/__init__.py +++ b/src/zeep/__init__.py @@ -2,4 +2,4 @@ from zeep.client import Client # noqa from zeep.transports import Transport # noqa from zeep.plugins import Plugin # noqa -__version__ = '0.23.0' +__version__ = '0.24.0' diff --git a/src/zeep/helpers.py b/src/zeep/helpers.py index 27df819..eec2e69 100644 --- a/src/zeep/helpers.py +++ b/src/zeep/helpers.py @@ -1,5 +1,7 @@ from collections import OrderedDict +from lxml import etree + from zeep.xsd.valueobjects import CompoundValue @@ -8,6 +10,9 @@ def serialize_object(obj): if obj is None: return obj + if isinstance(obj, etree._Element): + return obj + if isinstance(obj, list): return [serialize_object(sub) for sub in obj] diff --git a/src/zeep/wsdl/bindings/soap.py b/src/zeep/wsdl/bindings/soap.py index b700434..2f86319 100644 --- a/src/zeep/wsdl/bindings/soap.py +++ b/src/zeep/wsdl/bindings/soap.py @@ -193,7 +193,9 @@ class SoapBinding(Binding): soap_node = xmlelement.find('soap:binding', namespaces=cls.nsmap) transport = soap_node.get('transport') if transport != 'http://schemas.xmlsoap.org/soap/http': - raise NotImplementedError("Only soap/http is supported for now") + raise NotImplementedError( + "The binding transport %s is not supported (only soap/http)" % ( + transport)) default_style = soap_node.get('style', 'document') obj = cls(definitions.wsdl, name, port_name, transport, default_style) diff --git a/src/zeep/wsdl/messages/soap.py b/src/zeep/wsdl/messages/soap.py index b1b64b6..3875ecf 100644 --- a/src/zeep/wsdl/messages/soap.py +++ b/src/zeep/wsdl/messages/soap.py @@ -288,6 +288,8 @@ class SoapMessage(ConcreteMessage): for header_value in headers_value: if hasattr(header_value, '_xsd_elm'): header_value._xsd_elm.render(header, header_value) + elif hasattr(header_value, '_xsd_type'): + header_value._xsd_type.render(header, header_value) elif isinstance(header_value, etree._Element): header.append(header_value) else: @@ -298,7 +300,7 @@ class SoapMessage(ConcreteMessage): "_soapheaders only accepts a dictionary if the wsdl " "defines the headers.") headers_value = self.header(**headers_value) - self.header.render(header, headers_value) + self.header.type.render(header, headers_value) else: raise ValueError("Invalid value given to _soapheaders") @@ -315,9 +317,11 @@ class SoapMessage(ConcreteMessage): return {} def _resolve_header(self, info, definitions, parts): + name = etree.QName(self.nsmap['soap-env'], 'Header') + sequence = xsd.Sequence() if not info: - return xsd.Element(None, xsd.ComplexType(sequence)) + return xsd.Element(name, xsd.ComplexType(sequence)) for item in info: message_name = item['message'].text @@ -334,7 +338,7 @@ class SoapMessage(ConcreteMessage): else: element = xsd.Element(part_name, part.type) sequence.append(element) - return xsd.Element(None, xsd.ComplexType(sequence)) + return xsd.Element(name, xsd.ComplexType(sequence)) class DocumentMessage(SoapMessage): @@ -360,8 +364,10 @@ class DocumentMessage(SoapMessage): return {'body': result} def _resolve_body(self, info, definitions, parts): + name = etree.QName(self.nsmap['soap-env'], 'Body') + if not info or not parts: - return xsd.Element(None, xsd.ComplexType([])) + return xsd.Element(name, xsd.ComplexType([])) # If the part name is omitted then all parts are available under # the soap:body tag. Otherwise only the part with the given name. @@ -377,8 +383,7 @@ class DocumentMessage(SoapMessage): if len(sub_elements) > 1: self._is_body_wrapped = True - return xsd.Element( - None, xsd.ComplexType(xsd.All(sub_elements))) + return xsd.Element(name, xsd.ComplexType(xsd.All(sub_elements))) else: self._is_body_wrapped = False return sub_elements[0] @@ -404,8 +409,9 @@ class RpcMessage(SoapMessage): name and its namespace is the value of the namespace attribute. """ + name = etree.QName(self.nsmap['soap-env'], 'Body') if not info: - return xsd.Element(None, xsd.ComplexType([])) + return xsd.Element(name, xsd.ComplexType([])) namespace = info['namespace'] if self.type == 'input': diff --git a/src/zeep/wsdl/wsdl.py b/src/zeep/wsdl/wsdl.py index 3eb247c..ce14b66 100644 --- a/src/zeep/wsdl/wsdl.py +++ b/src/zeep/wsdl/wsdl.py @@ -358,7 +358,12 @@ class Definition(object): binding = None for binding_class in binding_classes: if binding_class.match(binding_node): - binding = binding_class.parse(self, binding_node) + + try: + binding = binding_class.parse(self, binding_node) + except NotImplementedError as exc: + logger.debug("Ignoring binding: %s", exc) + continue logger.debug("Adding binding: %s", binding.name.text) result[binding.name.text] = binding diff --git a/src/zeep/xsd/builtins.py b/src/zeep/xsd/builtins.py index 5219ae5..a4b64f3 100644 --- a/src/zeep/xsd/builtins.py +++ b/src/zeep/xsd/builtins.py @@ -179,6 +179,8 @@ class DateTime(_BuiltinType): @check_no_collection def xmlvalue(self, value): + if value.microsecond: + return isodate.isostrf.strftime(value, '%Y-%m-%dT%H:%M:%S.%f%Z') return isodate.isostrf.strftime(value, '%Y-%m-%dT%H:%M:%S%Z') def pythonvalue(self, value): @@ -191,6 +193,8 @@ class Time(_BuiltinType): @check_no_collection def xmlvalue(self, value): + if value.microsecond: + return isodate.isostrf.strftime(value, '%H:%M:%S.%f%Z') return isodate.isostrf.strftime(value, '%H:%M:%S%Z') def pythonvalue(self, value): @@ -512,6 +516,9 @@ class AnyType(_BuiltinType): if isinstance(value, AnyObject): value.xsd_type.render(parent, value.value) parent.set(xsi_ns('type'), value.xsd_type.qname) + elif hasattr(value, '_xsd_elm'): + value._xsd_elm.render(parent, value) + parent.set(xsi_ns('type'), value._xsd_elm.qname) else: parent.text = self.xmlvalue(value) @@ -525,7 +532,12 @@ class AnyType(_BuiltinType): return None if xsi_type and schema: - xsd_type = schema.get_type(xsi_type) + xsd_type = schema.get_type(xsi_type, fail_silently=True) + + # If we were unable to resolve a type for the xsi:type (due to + # buggy soap servers) then we just return the lxml element. + if not xsd_type: + return xmlelement.getchildren() # If the xsd_type is xsd:anyType then we will recurs so ignore # that. diff --git a/src/zeep/xsd/elements.py b/src/zeep/xsd/elements.py index e7e3d87..8a62188 100644 --- a/src/zeep/xsd/elements.py +++ b/src/zeep/xsd/elements.py @@ -28,13 +28,14 @@ class Base(object): def is_optional(self): return self.min_occurs == 0 - def parse_args(self, args): + def parse_args(self, args, index=0): result = {} if not args: - return result, args + return result, args, index - value = args.pop(0) - return {self.attr_name: value}, args + value = args[index] + index += 1 + return {self.attr_name: value}, args, index def parse_kwargs(self, kwargs, name, available_kwargs): raise NotImplementedError() @@ -146,7 +147,7 @@ class Any(Base): self._render_value_item(parent, value) def _render_value_item(self, parent, value): - if not value: + if value is None: # can be an lxml element return # Check if we received a proper value object. If we receive the wrong @@ -202,7 +203,9 @@ class Any(Base): class Element(Base): def __init__(self, name, type_=None, min_occurs=1, max_occurs=1, nillable=False, default=None, is_global=False, attr_name=None): - if name and not isinstance(name, etree.QName): + if name is None: + raise ValueError("name cannot be None", self.__class__) + if not isinstance(name, etree.QName): name = etree.QName(name) self.name = name.localname if name else None @@ -264,10 +267,10 @@ class Element(Base): """ context = context or XmlParserContext() instance_type = qname_attr(xmlelement, xsi_ns('type')) + xsd_type = None if instance_type: - xsd_type = schema.get_type(instance_type) - else: - xsd_type = self.type + xsd_type = schema.get_type(instance_type, fail_silently=True) + xsd_type = xsd_type or self.type return xsd_type.parse_xmlelement( xmlelement, schema, allow_none=allow_none, context=context) @@ -343,9 +346,6 @@ class Element(Base): elm.set(xsi_ns('nil'), 'true') return - if self.name is None: - return self.type.render(parent, value) - node = etree.SubElement(parent, self.qname) xsd_type = getattr(value, '_xsd_type', self.type) diff --git a/src/zeep/xsd/indicators.py b/src/zeep/xsd/indicators.py index 2ba117c..ccfe423 100644 --- a/src/zeep/xsd/indicators.py +++ b/src/zeep/xsd/indicators.py @@ -97,15 +97,15 @@ class OrderIndicator(Indicator, list): num += element.accept(values) return num - def parse_args(self, args): + def parse_args(self, args, index=0): result = {} for name, element in self.elements: - if not args: + if index >= len(args): break - arg = args.pop(0) - result[name] = arg + result[name] = args[index] + index += 1 - return result, args + return result, args, index def parse_kwargs(self, kwargs, name, available_kwargs): """Apply the given kwarg to the element. @@ -164,12 +164,7 @@ class OrderIndicator(Indicator, list): return self def render(self, parent, value): - """Create subelements in the given parent object. - - To make sure we render values only once the value items are copied - and the rendered attribute is removed from it once it is rendered. - - """ + """Create subelements in the given parent object.""" if not isinstance(value, list): values = [value] else: @@ -180,7 +175,6 @@ class OrderIndicator(Indicator, list): if name: if name in value: element_value = value[name] - del value[name] else: element_value = None else: @@ -256,8 +250,9 @@ class Choice(OrderIndicator): result = [] for i in max_occurs_iter(self.max_occurs): - if len(xmlelements) < 1: + if not xmlelements: break + for node in list(xmlelements): # Choose out of multiple @@ -409,7 +404,7 @@ class Choice(OrderIndicator): else: num = element.accept(values) nums.add(num) - return max(nums) + return max(nums) if nums else 0 def _find_element_to_render(self, value): """Return a tuple (element, value) for the best matching choice""" @@ -460,6 +455,9 @@ class Sequence(OrderIndicator): def parse_xmlelements(self, xmlelements, schema, name=None, context=None): result = [] for item in max_occurs_iter(self.max_occurs): + if not xmlelements: + break + item_result = OrderedDict() for elm_name, element in self.elements: item_subresult = element.parse_xmlelements( @@ -516,8 +514,8 @@ class Group(Indicator): return [('_value_1', self.child)] return self.child.elements - def parse_args(self, args): - return self.child.parse_args(args) + def parse_args(self, args, index=0): + return self.child.parse_args(args, index) def parse_kwargs(self, kwargs, name, available_kwargs): if self.accepts_multiple: diff --git a/src/zeep/xsd/schema.py b/src/zeep/xsd/schema.py index 000ac95..3f9da9f 100644 --- a/src/zeep/xsd/schema.py +++ b/src/zeep/xsd/schema.py @@ -106,7 +106,7 @@ class Schema(object): "No schema available for the namespace %r." ) % (qname.text, qname.namespace)) - def get_type(self, qname): + def get_type(self, qname, fail_silently=False): """Return a global xsd.Type object with the given qname""" qname = self._create_qname(qname) @@ -121,10 +121,15 @@ class Schema(object): schema = self._get_schema_document(qname.namespace) return schema.get_type(qname) except exceptions.NamespaceError: - raise exceptions.NamespaceError(( + message = ( "Unable to resolve type %s. " + "No schema available for the namespace %r." - ) % (qname.text, qname.namespace)) + ) % (qname.text, qname.namespace) + + if fail_silently: + logger.info(message) + else: + raise exceptions.NamespaceError(message) def get_group(self, qname): """Return a global xsd.Group object with the given qname""" diff --git a/src/zeep/xsd/types.py b/src/zeep/xsd/types.py index 7ce7522..87901b1 100644 --- a/src/zeep/xsd/types.py +++ b/src/zeep/xsd/types.py @@ -355,8 +355,11 @@ class ComplexType(Type): else: element.render(parent, element_value) - if xsd_type and xsd_type._xsd_name: - parent.set(xsi_ns('type'), xsd_type._xsd_name) + if xsd_type: + if xsd_type._xsd_name: + parent.set(xsi_ns('type'), xsd_type._xsd_name) + if xsd_type.qname: + parent.set(xsi_ns('type'), xsd_type.qname) def parse_kwargs(self, kwargs, name, available_kwargs): value = None diff --git a/src/zeep/xsd/valueobjects.py b/src/zeep/xsd/valueobjects.py index 2a508df..8cd5f18 100644 --- a/src/zeep/xsd/valueobjects.py +++ b/src/zeep/xsd/valueobjects.py @@ -55,6 +55,13 @@ class CompoundValue(object): def __contains__(self, key): return self.__values__.__contains__(key) + def __eq__(self, other): + if self.__class__ != other.__class__: + return False + + other_values = {key: other[key] for key in other} + return other_values == self.__values__ + def __len__(self): return self.__values__.__len__() @@ -118,21 +125,24 @@ def _process_signature(xsd_type, args, kwargs): if args: args = list(args) num_args = len(args) + index = 0 for element_name, element in xsd_type.elements_nested: - values, args = element.parse_args(args) + values, args, index = element.parse_args(args, index) if not values: break result.update(values) - if args: for attribute_name, attribute in xsd_type.attributes: - result[attribute_name] = args.pop(0) + if num_args <= index: + break + result[attribute_name] = args[index] + index += 1 - if args: - raise TypeError( - "__init__() takes at most %s positional arguments (%s given)" % ( - len(result), num_args)) + if num_args > index: + raise TypeError( + "__init__() takes at most %s positional arguments (%s given)" % ( + len(result), num_args)) # Process the named arguments (sequence/group/all/choice). The # available_kwargs set is modified in-place. diff --git a/tests/integration/test_http_post.py b/tests/integration/test_http_post.py new file mode 100644 index 0000000..dfcb992 --- /dev/null +++ b/tests/integration/test_http_post.py @@ -0,0 +1,34 @@ +import os + +import pytest +import requests_mock + +import zeep + +WSDL = os.path.join(os.path.dirname(__file__), 'test_http_post.wsdl') + + +@pytest.mark.requests +def test_get_urlreplacement(): + client = zeep.Client(WSDL) + + with requests_mock.mock() as m: + m.get('http://example.com/companyinfo/o1/EUR/', text='Hoi') + result = client.service.o1('EUR') + assert result == 'Hoi' + + history = m.request_history[0] + assert history._request.path_url == '/companyinfo/o1/EUR/' + + +@pytest.mark.requests +def test_post_mime_content(): + client = zeep.Client(WSDL, service_name='CompanyInfoService', port_name='Port3') + + with requests_mock.mock() as m: + m.post('http://example.com/companyinfo/o1', text='Hoi') + result = client.service.o1('EUR') + assert result == 'Hoi' + + history = m.request_history[0] + assert history._request.path_url == '/companyinfo/o1' diff --git a/tests/integration/test_http_post.wsdl b/tests/integration/test_http_post.wsdl new file mode 100644 index 0000000..5a33e1c --- /dev/null +++ b/tests/integration/test_http_post.wsdl @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/test_client.py b/tests/test_client.py index 122abc3..19dfb5c 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -16,6 +16,12 @@ def test_bind(): assert service +def test_unknown_transport(): + client_obj = client.Client('tests/wsdl_files/soap_transport_err.wsdl') + service = client_obj.bind() + assert service + + def test_bind_service(): client_obj = client.Client('tests/wsdl_files/soap.wsdl') service = client_obj.bind('StockQuoteService') @@ -174,13 +180,13 @@ def test_set_context_options_timeout(): @pytest.mark.requests def test_default_soap_headers(): - header = xsd.Element(None, xsd.ComplexType( + header = xsd.ComplexType( xsd.Sequence([ xsd.Element('{http://tests.python-zeep.org}name', xsd.String()), xsd.Element('{http://tests.python-zeep.org}password', xsd.String()), ]) - )) - header_value = header(name='ik', password='geheim') + ) + header_value = header(name='ik', password='foo') client_obj = client.Client('tests/wsdl_files/soap.wsdl') client_obj.set_default_soapheaders([header_value]) @@ -211,20 +217,20 @@ def test_default_soap_headers(): @pytest.mark.requests def test_default_soap_headers_extra(): - header = xsd.Element(None, xsd.ComplexType( + header = xsd.ComplexType( xsd.Sequence([ xsd.Element('{http://tests.python-zeep.org}name', xsd.String()), xsd.Element('{http://tests.python-zeep.org}password', xsd.String()), ]) - )) + ) header_value = header(name='ik', password='geheim') - extra_header = xsd.Element(None, xsd.ComplexType( + extra_header = xsd.ComplexType( xsd.Sequence([ xsd.Element('{http://tests.python-zeep.org}name', xsd.String()), xsd.Element('{http://tests.python-zeep.org}password', xsd.String()), ]) - )) + ) extra_header_value = extra_header(name='ik', password='geheim') client_obj = client.Client('tests/wsdl_files/soap.wsdl') diff --git a/tests/test_client_factory.py b/tests/test_client_factory.py new file mode 100644 index 0000000..92d5b1a --- /dev/null +++ b/tests/test_client_factory.py @@ -0,0 +1,35 @@ +import pytest + +from zeep import Client + + +def test_factory_namespace(): + client = Client('tests/wsdl_files/soap.wsdl') + factory = client.type_factory('http://example.com/stockquote.xsd') + obj = factory.Address(NameFirst='Michael', NameLast='van Tellingen') + assert obj.NameFirst == 'Michael' + assert obj.NameLast == 'van Tellingen' + + +def test_factory_ns_auto_prefix(): + client = Client('tests/wsdl_files/soap.wsdl') + factory = client.type_factory('ns0') + obj = factory.Address(NameFirst='Michael', NameLast='van Tellingen') + assert obj.NameFirst == 'Michael' + assert obj.NameLast == 'van Tellingen' + + +def test_factory_ns_custom_prefix(): + client = Client('tests/wsdl_files/soap.wsdl') + client.set_ns_prefix('sq', 'http://example.com/stockquote.xsd') + factory = client.type_factory('sq') + obj = factory.Address(NameFirst='Michael', NameLast='van Tellingen') + assert obj.NameFirst == 'Michael' + assert obj.NameLast == 'van Tellingen' + + +def test_factory_ns_unknown_prefix(): + client = Client('tests/wsdl_files/soap.wsdl') + + with pytest.raises(ValueError): + client.type_factory('bla') diff --git a/tests/test_helpers.py b/tests/test_helpers.py index a3ddb06..7165a5f 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -1,6 +1,6 @@ from lxml import etree -from tests.utils import load_xml +from tests.utils import assert_nodes_equal, load_xml from zeep import xsd from zeep.helpers import serialize_object @@ -111,3 +111,39 @@ def test_nested_complex_types(): assert isinstance(result, dict), type(result) assert isinstance(result['item'], dict), type(result['item']) assert result['item']['item_1'] == 'foo' + + +def test_serialize_any_array(): + custom_type = xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'authentication'), + xsd.ComplexType( + xsd.Sequence([ + xsd.Any(max_occurs=2), + ]) + )) + + any_obj = etree.Element('{http://tests.python-zeep.org}lxml') + etree.SubElement(any_obj, 'node').text = 'foo' + + obj = custom_type(any_obj) + + expected = """ + + + + foo + + + + """ + node = etree.Element('document') + custom_type.render(node, obj) + assert_nodes_equal(expected, node) + + schema = xsd.Schema() + obj = custom_type.parse(node.getchildren()[0], schema=schema) + result = serialize_object(obj) + + assert result == { + '_value_1': [any_obj], + } diff --git a/tests/test_main.py b/tests/test_main.py new file mode 100644 index 0000000..de98d04 --- /dev/null +++ b/tests/test_main.py @@ -0,0 +1,31 @@ +from mock import patch +from pretend import stub + +from zeep import __main__, client + + +def test_main_no_args(monkeypatch): + def mock_init(self, *args, **kwargs): + self.wsdl = stub(dump=lambda: None) + + monkeypatch.setattr(client.Client, '__init__', mock_init) + args = __main__.parse_arguments(['foo.wsdl']) + __main__.main(args) + + +def test_main_extract_auth(monkeypatch): + def mock_init(self, *args, **kwargs): + self.wsdl = stub(dump=lambda: None) + + monkeypatch.setattr(client.Client, '__init__', mock_init) + + with patch.object(__main__, 'Transport', autospec=True) as mock_transport: + args = __main__.parse_arguments( + ['http://user:secret@tests.python-zeep.org/foo.wsdl']) + + __main__.main(args) + + assert mock_transport.call_count == 1 + + args, kwargs = mock_transport.call_args + assert kwargs['http_auth'] == ('user', 'secret') diff --git a/tests/test_pprint.py b/tests/test_pprint.py new file mode 100644 index 0000000..1c53780 --- /dev/null +++ b/tests/test_pprint.py @@ -0,0 +1,34 @@ +from zeep.xsd import printer + + +def test_dict(): + pprint = printer.PrettyPrinter() + data = { + 'foo': 'bar', + 'foo_2': 'bar', + 'foo_3': 'bar', + 'foo_4': { + 'bar': '1', + 'bar': { + 'bala': 'qwe', + }, + 'x': [1, 2, 3, 4], + 'y': [], + } + } + pprint.pformat(data) + + +def test_list(): + pprint = printer.PrettyPrinter() + data = [ + { + 'foo': 'bar', + 'foo_2': 'bar', + }, + { + 'foo': 'bar', + 'foo_2': 'bar', + }, + ] + pprint.pformat(data) diff --git a/tests/test_transports.py b/tests/test_transports.py new file mode 100644 index 0000000..63f3e00 --- /dev/null +++ b/tests/test_transports.py @@ -0,0 +1,29 @@ +import pytest +import requests_mock +from pretend import stub + +from zeep import cache, transports + + +@pytest.mark.requests +def test_default_cache(): + transport = transports.Transport() + assert isinstance(transport.cache, cache.SqliteCache) + + +@pytest.mark.requests +def test_no_cache(): + transport = transports.Transport(cache=None) + assert transport.cache is None + + +@pytest.mark.requests +def test_load(): + cache = stub(get=lambda url: None, add=lambda url, content: None) + transport = transports.Transport(cache=cache) + + with requests_mock.mock() as m: + m.get('http://tests.python-zeep.org/test.xml', text='x') + result = transport.load('http://tests.python-zeep.org/test.xml') + + assert result == b'x' diff --git a/tests/test_wsa.py b/tests/test_wsa.py new file mode 100644 index 0000000..a53d990 --- /dev/null +++ b/tests/test_wsa.py @@ -0,0 +1,268 @@ +import uuid + +from pretend import stub +from six import StringIO + +from tests.utils import DummyTransport, assert_nodes_equal +from zeep import wsa, wsdl, Client + + +def test_require_wsa(recwarn, monkeypatch): + monkeypatch.setattr( + uuid, 'uuid4', lambda: uuid.UUID('ada686f9-5995-4088-bea4-239f694b2eaf')) + + wsdl_main = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Test service + + + + + + """.strip()) + + client = stub(plugins=[], wsse=None) + + transport = DummyTransport() + client = Client(wsdl_main, transport=transport) + binding = client.wsdl.services.get('TestService').ports.get('TestPortType').binding + + envelope, headers = binding._create( + 'TestOperation1', + args=['foo'], + kwargs={}, + client=client, + options={'address': 'http://tests.python-zeep.org/test'}) + expected = """ + + + urn:dummyRequest + urn:uuid:ada686f9-5995-4088-bea4-239f694b2eaf + http://tests.python-zeep.org/test + + + foo + + + """ + assert_nodes_equal(expected, envelope) + + +def test_force_wsa(recwarn, monkeypatch): + monkeypatch.setattr( + uuid, 'uuid4', lambda: uuid.UUID('ada686f9-5995-4088-bea4-239f694b2eaf')) + + wsdl_main = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Test service + + + + + + """.strip()) + + transport = DummyTransport() + client = Client(wsdl_main, transport=transport, plugins=[wsa.WsAddressingPlugin()]) + binding = client.wsdl.services.get('TestService').ports.get('TestPortType').binding + + envelope, headers = binding._create( + 'TestOperation1', + args=['foo'], + kwargs={}, + client=client, + options={'address': 'http://tests.python-zeep.org/test'}) + expected = """ + + + urn:dummyRequest + urn:uuid:ada686f9-5995-4088-bea4-239f694b2eaf + http://tests.python-zeep.org/test + + + foo + + + """ + assert_nodes_equal(expected, envelope) + + +def test_force_wsa_soap12(recwarn, monkeypatch): + monkeypatch.setattr( + uuid, 'uuid4', lambda: uuid.UUID('ada686f9-5995-4088-bea4-239f694b2eaf')) + + wsdl_main = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Test service + + + + + + """.strip()) + + client = stub(plugins=[wsa.WsAddressingPlugin()], wsse=None) + + transport = DummyTransport() + doc = wsdl.Document(wsdl_main, transport) + binding = doc.services.get('TestService').ports.get('TestPortType').binding + + envelope, headers = binding._create( + 'TestOperation1', + args=['foo'], + kwargs={}, + client=client, + options={'address': 'http://tests.python-zeep.org/test'}) + expected = """ + + + urn:dummyRequest + urn:uuid:ada686f9-5995-4088-bea4-239f694b2eaf + http://tests.python-zeep.org/test + + + foo + + + + """ + assert_nodes_equal(expected, envelope) + + assert headers['Content-Type'] == ( + 'application/soap+xml; charset=utf-8; action="urn:dummyRequest"') diff --git a/tests/test_wsdl_arrays.py b/tests/test_wsdl_arrays.py new file mode 100644 index 0000000..f6ad722 --- /dev/null +++ b/tests/test_wsdl_arrays.py @@ -0,0 +1,360 @@ +import io + +from lxml import etree + +from tests.utils import DummyTransport, assert_nodes_equal, load_xml +from zeep import xsd + + +def get_transport(): + transport = DummyTransport() + transport.bind( + 'http://schemas.xmlsoap.org/soap/encoding/', + load_xml(io.open('tests/wsdl_files/soap-enc.xsd', 'r').read().encode('utf-8'))) + return transport + + +def test_simple_type(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + """), transport=get_transport()) + + ArrayOfString = schema.get_type('ns0:ArrayOfString') + print(ArrayOfString.__dict__) + + value = ArrayOfString(['item', 'and', 'even', 'more', 'items']) + + node = etree.Element('document') + ArrayOfString.render(node, value) + + expected = """ + + item + and + even + more + items + + """ # noqa + + assert_nodes_equal(expected, node) + + +def test_complex_type(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + + + + + """), transport=get_transport()) + + ArrayOfObject = schema.get_type('ns0:ArrayOfObject') + ArrayObject = schema.get_type('ns0:ArrayObject') + + value = ArrayOfObject([ + ArrayObject(attr_1='attr-1', attr_2='attr-2'), + ArrayObject(attr_1='attr-3', attr_2='attr-4'), + ArrayObject(attr_1='attr-5', attr_2='attr-6'), + ]) + + node = etree.Element('document') + ArrayOfObject.render(node, value) + + expected = """ + + + attr-1 + attr-2 + + + attr-3 + attr-4 + + + attr-5 + attr-6 + + + """ + assert_nodes_equal(expected, node) + + +def test_complex_type_without_name(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + + """), transport=get_transport()) + + ArrayOfObject = schema.get_type('ns0:ArrayOfObject') + ArrayObject = schema.get_type('ns0:ArrayObject') + + value = ArrayOfObject([ + ArrayObject(attr_1='attr-1', attr_2='attr-2'), + ArrayObject(attr_1='attr-3', attr_2='attr-4'), + ArrayObject(attr_1='attr-5', attr_2='attr-6'), + ]) + + node = etree.Element('document') + ArrayOfObject.render(node, value) + + expected = """ + + + attr-1 + attr-2 + + + attr-3 + attr-4 + + + attr-5 + attr-6 + + + """ + assert_nodes_equal(expected, node) + + +def test_soap_array_parse_remote_ns(): + transport = DummyTransport() + transport.bind( + 'http://schemas.xmlsoap.org/soap/encoding/', + load_xml(io.open('tests/wsdl_files/soap-enc.xsd', 'r').read().encode('utf-8'))) + + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + + + + + + + + + """), transport) + + doc = load_xml(""" + + + NL + The Netherlands + + + """) + + elm = schema.get_element('ns0:countries') + data = elm.parse(doc, schema) + + assert data._value_1[0].code == 'NL' + assert data._value_1[0].name == 'The Netherlands' + + +def test_wsdl_array_type(): + transport = DummyTransport() + transport.bind( + 'http://schemas.xmlsoap.org/soap/encoding/', + load_xml(io.open('tests/wsdl_files/soap-enc.xsd', 'r').read().encode('utf-8'))) + + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + + """), transport) + array_elm = schema.get_element('{http://tests.python-zeep.org/}array') + + item_type = schema.get_type('{http://tests.python-zeep.org/}base') + item_1 = item_type(item_1='foo_1', item_2='bar_1') + item_2 = item_type(item_1='foo_2', item_2='bar_2') + + # array = array_elm([ + # xsd.AnyObject(item_type, item_1), + # xsd.AnyObject(item_type, item_2), + # ]) + + array = array_elm([item_1, item_2]) + node = etree.Element('document') + assert array_elm.signature() == ( + '_value_1: base[], arrayType: xsd:string, offset: arrayCoordinate, ' + + 'id: xsd:ID, href: xsd:anyURI, _attr_1: {}') + array_elm.render(node, array) + expected = """ + + + + foo_1 + bar_1 + + + foo_2 + bar_2 + + + + """ + assert_nodes_equal(expected, node) + + +def test_soap_array_parse(): + transport = DummyTransport() + transport.bind( + 'http://schemas.xmlsoap.org/soap/encoding/', + load_xml(io.open('tests/wsdl_files/soap-enc.xsd', 'r').read().encode('utf-8'))) + + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + """), transport) + + doc = load_xml(""" + + + flag1 + value1 + + + flag2 + value2 + + + """) + + elm = schema.get_element('ns0:FlagDetailsList') + data = elm.parse(doc, schema) + assert data.FlagDetailsStruct[0].Name == 'flag1' + assert data.FlagDetailsStruct[0].Value == 'value1' + assert data.FlagDetailsStruct[1].Name == 'flag2' + assert data.FlagDetailsStruct[1].Value == 'value2' diff --git a/tests/test_wsdl_messages_document.py b/tests/test_wsdl_messages_document.py new file mode 100644 index 0000000..5aa18a7 --- /dev/null +++ b/tests/test_wsdl_messages_document.py @@ -0,0 +1,1271 @@ +from lxml import etree +from six import StringIO + +from tests.utils import assert_nodes_equal, load_xml +from zeep import xsd +from zeep.wsdl import wsdl + + +def test_parse(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + + assert operation.input.body.signature() == 'xsd:string' + assert operation.input.header.signature() == '' + assert operation.input.envelope.signature() == 'body: xsd:string, header: {}' + assert operation.input.signature(as_output=False) == 'xsd:string' + + assert operation.output.body.signature() == 'xsd:string' + assert operation.output.header.signature() == '' + assert operation.output.envelope.signature() == 'body: xsd:string, header: {}' + assert operation.output.signature(as_output=True) == 'body: xsd:string, header: {}' + + +def test_empty_input_parse(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + + assert operation.input.body.signature() == '' + assert operation.input.header.signature() == '' + assert operation.input.envelope.signature() == 'body: {}, header: {}' + assert operation.input.signature(as_output=False) == '' + + +def test_parse_with_header(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + + assert operation.input.body.signature() == 'xsd:string' + assert operation.input.header.signature() == 'auth: RequestHeader()' + assert operation.input.envelope.signature() == 'body: xsd:string, header: {auth: RequestHeader()}' # noqa + assert operation.input.signature(as_output=False) == 'xsd:string, _soapheaders={auth: RequestHeader()}' # noqa + + assert operation.output.body.signature() == 'xsd:string' + assert operation.output.header.signature() == 'auth: ResponseHeader()' + assert operation.output.envelope.signature() == 'body: xsd:string, header: {auth: ResponseHeader()}' # noqa + assert operation.output.signature(as_output=True) == 'body: xsd:string, header: {auth: ResponseHeader()}' # noqa + + +def test_parse_with_header_type(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + + assert operation.input.body.signature() == 'xsd:string' + assert operation.input.header.signature() == 'auth: RequestHeaderType' + assert operation.input.envelope.signature() == 'body: xsd:string, header: {auth: RequestHeaderType}' # noqa + assert operation.input.signature(as_output=False) == 'xsd:string, _soapheaders={auth: RequestHeaderType}' # noqa + + assert operation.output.body.signature() == 'xsd:string' + assert operation.output.header.signature() == 'auth: ResponseHeaderType' + assert operation.output.envelope.signature() == 'body: xsd:string, header: {auth: ResponseHeaderType}' # noqa + assert operation.output.signature(as_output=True) == 'body: xsd:string, header: {auth: ResponseHeaderType}' # noqa + + + + + +def test_parse_with_header_other_message(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + + assert operation.input.header.signature() == 'header: RequestHeader()' + assert operation.input.body.signature() == 'xsd:string' + + header = root.types.get_element( + '{http://tests.python-zeep.org/tns}RequestHeader' + )('foo') + serialized = operation.input.serialize( + 'ah1', _soapheaders={'header': header}) + expected = """ + + + + foo + + + ah1 + + + """ + assert_nodes_equal(expected, serialized.content) + + +def test_serialize(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + + serialized = operation.input.serialize(arg1='ah1', arg2='ah2') + expected = """ + + + + + ah1 + ah2 + + + + """ + assert_nodes_equal(expected, serialized.content) + + +def test_serialize_with_header(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + + AuthHeader = root.types.get_element('{http://tests.python-zeep.org/tns}Authentication') + auth_header = AuthHeader(username='mvantellingen') + + serialized = operation.input.serialize( + arg1='ah1', arg2='ah2', _soapheaders=[auth_header]) + serialized = operation.input.serialize( + arg1='ah1', arg2='ah2', _soapheaders=[auth_header]) + expected = """ + + + + + mvantellingen + + + + + ah1 + ah2 + + + + """ + assert_nodes_equal(expected, serialized.content) + + +def test_serialize_with_headers_simple(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + + header = xsd.ComplexType( + xsd.Sequence([ + xsd.Element('{http://www.w3.org/2005/08/addressing}Action', xsd.String()), + xsd.Element('{http://www.w3.org/2005/08/addressing}To', xsd.String()), + ]) + ) + header_value = header(Action='doehet', To='server') + serialized = operation.input.serialize( + arg1='ah1', arg2='ah2', + _soapheaders=[header_value]) + expected = """ + + + + doehet + server + + + + ah1 + ah2 + + + + """ + assert_nodes_equal(expected, serialized.content) + + +def test_serialize_with_header_and_custom_mixed(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + + header = root.types.get_element( + '{http://tests.python-zeep.org/tns}Authentication' + ) + header_1 = header(username='mvantellingen') + + header = xsd.Element( + '{http://test.python-zeep.org/custom}custom', + xsd.ComplexType([ + xsd.Element('{http://test.python-zeep.org/custom}foo', xsd.String()), + ]) + ) + header_2 = header(foo='bar') + + serialized = operation.input.serialize( + arg1='ah1', arg2='ah2', + _soapheaders=[header_1, header_2]) + expected = """ + + + + + mvantellingen + + + bar + + + + + ah1 + ah2 + + + + """ + assert_nodes_equal(expected, serialized.content) + + +def test_serializer_with_header_custom_elm(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + + header = xsd.Element( + '{http://test.python-zeep.org/custom}auth', + xsd.ComplexType([ + xsd.Element('{http://test.python-zeep.org/custom}username', xsd.String()), + ]) + ) + + serialized = operation.input.serialize( + arg1='ah1', arg2='ah2', _soapheaders=[header(username='mvantellingen')]) + + expected = """ + + + + + mvantellingen + + + + + ah1 + ah2 + + + + """ + assert_nodes_equal(expected, serialized.content) + + +def test_serializer_with_header_custom_xml(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + + header_value = etree.Element('{http://test.python-zeep.org/custom}auth') + etree.SubElement( + header_value, '{http://test.python-zeep.org/custom}username' + ).text = 'mvantellingen' + + serialized = operation.input.serialize( + arg1='ah1', arg2='ah2', _soapheaders=[header_value]) + + expected = """ + + + + + mvantellingen + + + + + ah1 + ah2 + + + + """ + assert_nodes_equal(expected, serialized.content) + + +def test_deserialize(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + + response_body = load_xml(""" + + + + + ah1 + ah2 + + + + """) + result = operation.process_reply(response_body) + assert result.arg1 == 'ah1' + assert result.arg2 == 'ah2' + + +def test_deserialize_no_content(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + + response_body = load_xml(""" + + + + + + + """) + result = operation.process_reply(response_body) + assert result is None + + +def test_deserialize_choice(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + + response_body = load_xml(""" + + + + + ah1 + + + + """) + result = operation.process_reply(response_body) + assert result.arg1 == 'ah1' + + +def test_deserialize_one_part(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + + response_body = load_xml(""" + + + + + mvantellingen + + + + + ah1 + ah2 + + + + """) # noqa + + serialized = operation.process_reply(response_body) + assert serialized.arg1 == 'ah1' + assert serialized.arg2 == 'ah2' + + +def test_deserialize_with_headers(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + + response_body = load_xml(""" + + + + + mvantellingen + + foo + + + + ah1 + + + ah2 + + + + """) # noqa + + serialized = operation.process_reply(response_body) + + assert operation.output.signature(as_output=True) == ( + 'body: {request_1: Request1(), request_2: Request2()}, header: {header_1: Header1(), header_2: Header2()}') # noqa + assert serialized.body.request_1.arg1 == 'ah1' + assert serialized.body.request_2.arg2 == 'ah2' + assert serialized.header.header_1.username == 'mvantellingen' + + +def test_serialize_any_type(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + + serialized = operation.input.serialize( + arg1=xsd.AnyObject(xsd.String(), 'ah1')) + expected = """ + + + + + ah1 + + + + """ + assert_nodes_equal(expected, serialized.content) + deserialized = operation.input.deserialize(serialized.content) + + assert deserialized == 'ah1' diff --git a/tests/test_wsdl_messages_http.py b/tests/test_wsdl_messages_http.py new file mode 100644 index 0000000..aa40d25 --- /dev/null +++ b/tests/test_wsdl_messages_http.py @@ -0,0 +1,390 @@ +from six import StringIO + +from tests.utils import assert_nodes_equal, load_xml +from zeep.wsdl import wsdl + + +## +# URLEncoded Message +# +def test_urlencoded_serialize(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + + assert operation.input.body.signature() == 'arg1: xsd:string, arg2: xsd:string' + assert operation.input.signature(as_output=False) == 'arg1: xsd:string, arg2: xsd:string' + + assert operation.output.body.signature() == 'Body: xsd:string' + assert operation.output.signature(as_output=True) == 'xsd:string' + + serialized = operation.input.serialize(arg1='ah1', arg2='ah2') + assert serialized.headers == {'Content-Type': 'text/xml; charset=utf-8'} + assert serialized.path == 'test-operation' + assert serialized.content == {'arg1': 'ah1', 'arg2': 'ah2'} + + +## +# URLReplacement Message +# +def test_urlreplacement_serialize(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + + assert operation.input.body.signature() == 'arg1: xsd:string, arg2: xsd:string' + assert operation.input.signature(as_output=False) == 'arg1: xsd:string, arg2: xsd:string' + + assert operation.output.body.signature() == 'Body: xsd:string' + assert operation.output.signature(as_output=True) == 'xsd:string' + + serialized = operation.input.serialize(arg1='ah1', arg2='ah2') + assert serialized.headers == {'Content-Type': 'text/xml; charset=utf-8'} + assert serialized.path == 'test-operation/ah1/ah2/' + assert serialized.content == '' + + +## +# MimeContent Message +# +def test_mime_content_serialize_form_urlencoded(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + + assert operation.input.body.signature() == 'arg1: xsd:string, arg2: xsd:string' + assert operation.input.signature(as_output=False) == 'arg1: xsd:string, arg2: xsd:string' + + assert operation.output.body.signature() == 'Body: xsd:string' + assert operation.output.signature(as_output=True) == 'xsd:string' + + serialized = operation.input.serialize(arg1='ah1', arg2='ah2') + assert serialized.headers == {'Content-Type': 'application/x-www-form-urlencoded'} + assert serialized.path == 'test-operation' + assert serialized.content == 'arg1=ah1&arg2=ah2' + + +def test_mime_content_serialize_text_xml(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + + assert operation.input.body.signature() == 'arg1: xsd:string, arg2: xsd:string' + assert operation.input.signature(as_output=False) == 'arg1: xsd:string, arg2: xsd:string' + + assert operation.output.body.signature() == 'Body: xsd:string' + assert operation.output.signature(as_output=True) == 'xsd:string' + + serialized = operation.input.serialize(arg1='ah1', arg2='ah2') + assert serialized.headers == {'Content-Type': 'text/xml'} + assert serialized.path == 'test-operation' + assert_nodes_equal( + load_xml(serialized.content), + load_xml("ah1ah2")) + + +def test_mime_content_no_parts(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + + assert operation.input.signature() == '' + + serialized = operation.input.serialize() + assert serialized.content == '' + + +def test_mime_xml_deserialize(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + + assert operation.input.signature() == 'arg1: xsd:string, arg2: xsd:string' + assert operation.output.signature(as_output=True) == ( + 'item_1: xsd:string, item_2: xsd:string') + + node = """ + + foo + bar + + """.strip() + + serialized = operation.output.deserialize(node) + assert serialized.item_1 == 'foo' + assert serialized.item_2 == 'bar' + + +def test_mime_multipart_parse(): + load_xml(""" + + + + + + + + + + + + + + + """) diff --git a/tests/test_wsdl_messages_rpc.py b/tests/test_wsdl_messages_rpc.py new file mode 100644 index 0000000..123683d --- /dev/null +++ b/tests/test_wsdl_messages_rpc.py @@ -0,0 +1,434 @@ +import io + +from six import StringIO + +from tests.utils import DummyTransport, assert_nodes_equal, load_xml +from zeep.wsdl import wsdl + + +def test_serialize(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + + assert operation.input.signature() == 'arg1: xsd:string, arg2: xsd:string' + + serialized = operation.input.serialize(arg1='ah1', arg2='ah2') + expected = """ + + + + + ah1 + ah2 + + + + """ + assert_nodes_equal(expected, serialized.content) + + +def test_serialize_empty_input(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + + serialized = operation.input.serialize() + expected = """ + + + + + + + """ + assert_nodes_equal(expected, serialized.content) + + +def test_deserialize(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('TestOperation') + + document = load_xml(""" + + + + ah1 + + + + """) + assert operation.output.signature(True) == 'body: {result: xsd:string}, header: {}' + result = operation.output.deserialize(document) + assert result == 'ah1' + + +def test_wsdl_array_of_simple_types(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) # noqa + + transport = DummyTransport() + transport.bind( + 'http://schemas.xmlsoap.org/soap/encoding/', + load_xml(io.open('tests/wsdl_files/soap-enc.xsd', 'r').read().encode('utf-8'))) + root = wsdl.Document(wsdl_content, transport) + + binding = root.bindings['{http://tests.python-zeep.org/tns}SimpleTypeArrayBinding'] + operation = binding.get('getSimpleArray') + + document = load_xml(""" + + + + + item + and + even + more + items + + + + + """) + + deserialized = operation.output.deserialize(document) + assert deserialized == ['item', 'and', 'even', 'more', 'items'] + + +def test_handle_incorrectly_qualified(): + # Based on #176 + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + transport = DummyTransport() + root = wsdl.Document(wsdl_content, transport) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestSoapBinding'] + operation = binding.get('getItem') + + document = load_xml(""" + + + + foobar + + + + """) + deserialized = operation.output.deserialize(document) + assert deserialized == 'foobar' + + +def test_deserialize_rpc_literal(): + # Based on #219 + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + transport = DummyTransport() + root = wsdl.Document(wsdl_content, transport) + + binding = root.bindings['{http://tests.python-zeep.org/tns}TestSoapBinding'] + operation = binding.get('getItem') + + document = load_xml(""" + + + + foobar + + + + """) + deserialized = operation.output.deserialize(document) + assert deserialized == 'foobar' + + +def test_deserialize(): + wsdl_content = StringIO(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + root = wsdl.Document(wsdl_content, None) + binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding'] + operation = binding.get('clearFoo') + + document = load_xml(""" + + + + + + + """) + result = operation.output.deserialize(document) + assert result is None diff --git a/tests/test_wsdl_soap.py b/tests/test_wsdl_soap.py new file mode 100644 index 0000000..04a9fc9 --- /dev/null +++ b/tests/test_wsdl_soap.py @@ -0,0 +1,110 @@ +from lxml import etree + +from tests.utils import load_xml +from zeep.exceptions import Fault +from zeep.wsdl import bindings + + +def test_soap11_process_error(): + response = load_xml(""" + + + + fault-code + fault-string + + + detail-message + detail-code + + + + + + """) + binding = bindings.Soap11Binding( + wsdl=None, name=None, port_name=None, transport=None, + default_style=None) + + try: + binding.process_error(response, None) + assert False + except Fault as exc: + assert exc.message == 'fault-string' + assert exc.code == 'fault-code' + assert exc.actor is None + assert exc.subcodes is None + assert 'detail-message' in etree.tostring(exc.detail).decode('utf-8') + + +def test_soap12_process_error(): + response = """ + + + + + fault-code + %s + + + us-error + nl-error + + + + Invalid credit card details + 999 + + + + + + """ + subcode = """ + + %s + %s + + """ + binding = bindings.Soap12Binding( + wsdl=None, name=None, port_name=None, transport=None, + default_style=None) + + try: + binding.process_error(load_xml(response % ""), None) + assert False + except Fault as exc: + assert exc.message == 'us-error' + assert exc.code == 'fault-code' + assert exc.subcodes == [] + + try: + binding.process_error( + load_xml(response % subcode % ("fault-subcode1", "")), None) + assert False + except Fault as exc: + assert exc.message == 'us-error' + assert exc.code == 'fault-code' + assert len(exc.subcodes) == 1 + assert exc.subcodes[0].namespace == 'http://example.com/example1' + assert exc.subcodes[0].localname == 'fault-subcode1' + + try: + binding.process_error( + load_xml(response % subcode % ("fault-subcode1", subcode % ("ex:fault-subcode2", ""))), + None) + assert False + except Fault as exc: + assert exc.message == 'us-error' + assert exc.code == 'fault-code' + assert len(exc.subcodes) == 2 + assert exc.subcodes[0].namespace == 'http://example.com/example1' + assert exc.subcodes[0].localname == 'fault-subcode1' + assert exc.subcodes[1].namespace == 'http://example.com/example2' + assert exc.subcodes[1].localname == 'fault-subcode2' diff --git a/tests/test_wsse_username.py b/tests/test_wsse_username.py new file mode 100644 index 0000000..690b818 --- /dev/null +++ b/tests/test_wsse_username.py @@ -0,0 +1,238 @@ +import datetime +import os + +import pytest +import requests_mock +from freezegun import freeze_time + +from tests.utils import assert_nodes_equal, load_xml +from zeep import client +from zeep.wsse.username import UsernameToken + + +@pytest.mark.requests +def test_integration(): + client_obj = client.Client( + 'tests/wsdl_files/soap.wsdl', + wsse=UsernameToken('username', 'password')) + + response = """ + + + + + + 120.123 + + + + """.strip() + + with requests_mock.mock() as m: + m.post('http://example.com/stockquote', text=response) + result = client_obj.service.GetLastTradePrice('foobar') + assert result == 120.123 + + +def test_password_text(): + envelope = load_xml(""" + + + + foobar + + + + + """) + + token = UsernameToken('michael', 'geheim') + envelope, headers = token.sign(envelope, {}) + expected = """ + + + + + michael + geheim + + + + + + foobar + + + + + """ # noqa + assert_nodes_equal(envelope, expected) + + +@freeze_time('2016-05-08 12:00:00') +def test_password_digest(monkeypatch): + monkeypatch.setattr(os, 'urandom', lambda x: b'mocked-random') + + envelope = load_xml(""" + + + + foobar + + + + + """) + + token = UsernameToken('michael', 'geheim', use_digest=True) + envelope, headers = token.sign(envelope, {}) + expected = """ + + + + + michael + hVicspAQSg70JNhe67OHqD9gexc= + bW9ja2VkLXJhbmRvbQ== + 2016-05-08T12:00:00+00:00 + + + + + + foobar + + + + + """ # noqa + assert_nodes_equal(envelope, expected) + + +@freeze_time('2016-05-08 12:00:00') +def test_password_digest_custom(monkeypatch): + monkeypatch.setattr(os, 'urandom', lambda x: b'mocked-random') + + envelope = load_xml(""" + + + + foobar + + + + + """) + + created = datetime.datetime(2016, 6, 4, 20, 10) + token = UsernameToken( + 'michael', password_digest='12345', use_digest=True, + nonce='iets', created=created) + envelope, headers = token.sign(envelope, {}) + expected = """ + + + + + michael + 12345 + aWV0cw== + 2016-06-04T20:10:00+00:00 + + + + + + foobar + + + + + """ # noqa + assert_nodes_equal(envelope, expected) + + +def test_password_prepared(): + envelope = load_xml(""" + + + + + + + + + foobar + + + + + """) # noqa + + token = UsernameToken('michael', 'geheim') + envelope, headers = token.sign(envelope, {}) + expected = """ + + + + + michael + geheim + + + + + + foobar + + + + + """ # noqa + assert_nodes_equal(envelope, expected) diff --git a/tests/test_wsse_utils.py b/tests/test_wsse_utils.py new file mode 100644 index 0000000..8b3a3aa --- /dev/null +++ b/tests/test_wsse_utils.py @@ -0,0 +1,25 @@ +from lxml import etree + +from zeep.wsse import utils + + +def test_get_security_header(): + doc = etree.fromstring(""" + + + + foobar + + + + + """.strip()) + + element = utils.get_security_header(doc) + assert element.tag == '{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}Security' # noqa diff --git a/tests/test_xsd_any.py b/tests/test_xsd_any.py new file mode 100644 index 0000000..4137b34 --- /dev/null +++ b/tests/test_xsd_any.py @@ -0,0 +1,280 @@ +import datetime + +import pytest +from lxml import etree + +from tests.utils import assert_nodes_equal, load_xml +from zeep import xsd + + +def get_any_schema(): + return xsd.Schema(load_xml(""" + + + + + + + + + + + + """)) + + +def test_any_simple(): + schema = get_any_schema() + + item_elm = schema.get_element('{http://tests.python-zeep.org/}item') + assert isinstance(item_elm.type, xsd.String) + + container_elm = schema.get_element('{http://tests.python-zeep.org/}container') + + # Create via arg + obj = container_elm(xsd.AnyObject(item_elm, item_elm('argh'))) + node = etree.Element('document') + container_elm.render(node, obj) + expected = """ + + + argh + + + """ + assert_nodes_equal(expected, node) + item = container_elm.parse(node.getchildren()[0], schema) + assert item._value_1 == 'argh' + + # Create via kwarg _value_1 + obj = container_elm(_value_1=xsd.AnyObject(item_elm, item_elm('argh'))) + node = etree.Element('document') + container_elm.render(node, obj) + expected = """ + + + argh + + + """ + assert_nodes_equal(expected, node) + item = container_elm.parse(node.getchildren()[0], schema) + assert item._value_1 == 'argh' + + +def test_any_value_element_tree(): + schema = get_any_schema() + + item = etree.Element('{http://tests.python-zeep.org}lxml') + etree.SubElement(item, 'node').text = 'foo' + + container_elm = schema.get_element('{http://tests.python-zeep.org/}container') + + # Create via arg + obj = container_elm(item) + node = etree.Element('document') + container_elm.render(node, obj) + expected = """ + + + + foo + + + + """ + assert_nodes_equal(expected, node) + item = container_elm.parse(node.getchildren()[0], schema) + assert isinstance(item._value_1, etree._Element) + assert item._value_1.tag == '{http://tests.python-zeep.org}lxml' + + +def test_any_value_invalid(): + schema = get_any_schema() + + class SomeThing(object): + pass + + container_elm = schema.get_element('{http://tests.python-zeep.org/}container') + + # Create via arg + item = SomeThing() + obj = container_elm(item) + node = etree.Element('document') + + with pytest.raises(TypeError): + container_elm.render(node, obj) + + +def test_any_with_ref(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + """)) + + item_elm = schema.get_element('{http://tests.python-zeep.org/}item') + assert isinstance(item_elm.type, xsd.String) + + container_elm = schema.get_element('{http://tests.python-zeep.org/}container') + obj = container_elm( + item='bar', + _value_1=xsd.AnyObject(item_elm, item_elm('argh'))) + + node = etree.Element('document') + container_elm.render(node, obj) + expected = """ + + + bar + argh + + + """ + assert_nodes_equal(expected, node) + item = container_elm.parse(node.getchildren()[0], schema) + assert item.item == 'bar' + assert item._value_1 == 'argh' + + +def test_element_any_parse(): + node = load_xml(""" + + + + + + + + + + """) + + schema = xsd.Schema(node) + + node = load_xml(""" + + + text + + + """) + + elm = schema.get_element('ns0:container') + elm.parse(node, schema) + + +def test_element_any_type(): + node = etree.fromstring(""" + + + + + + + + + + + """.strip()) + schema = xsd.Schema(node) + + container_elm = schema.get_element('{http://tests.python-zeep.org/}container') + obj = container_elm(something='bar') + + node = etree.Element('document') + container_elm.render(node, obj) + expected = """ + + + bar + + + """ + assert_nodes_equal(expected, node) + item = container_elm.parse(node.getchildren()[0], schema) + assert item.something == 'bar' + + +def test_any_in_nested_sequence(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + + + + + """)) # noqa + + container_elm = schema.get_element('{http://tests.python-zeep.org/}container') + assert container_elm.signature() == ( + 'items: {_value_1: ANY}, version: xsd:string, _value_1: ANY[]') + + something = schema.get_element('{http://tests.python-zeep.org/}something') + foobar = schema.get_element('{http://tests.python-zeep.org/}foobar') + + any_1 = xsd.AnyObject(something, datetime.date(2016, 7, 4)) + any_2 = xsd.AnyObject(foobar, True) + obj = container_elm( + items={'_value_1': any_1}, version='str1234', _value_1=[any_1, any_2]) + + node = etree.Element('document') + container_elm.render(node, obj) + expected = """ + + + + 2016-07-04 + + str1234 + 2016-07-04 + true + + + """ + assert_nodes_equal(expected, node) + item = container_elm.parse(node.getchildren()[0], schema) + assert item.items._value_1 == datetime.date(2016, 7, 4) + assert item.version == 'str1234' + assert item._value_1 == [datetime.date(2016, 7, 4), True] diff --git a/tests/test_xsd_attributes.py b/tests/test_xsd_attributes.py new file mode 100644 index 0000000..3e42458 --- /dev/null +++ b/tests/test_xsd_attributes.py @@ -0,0 +1,420 @@ +from collections import OrderedDict + +from lxml import etree + +from tests.utils import assert_nodes_equal, load_xml +from zeep import xsd + + +def test_anyattribute(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + """)) + + container_elm = schema.get_element('{http://tests.python-zeep.org/}container') + assert container_elm.signature() == ( + 'foo: xsd:string, _attr_1: {}') + obj = container_elm(foo='bar', _attr_1=OrderedDict([ + ('hiep', 'hoi'), ('hoi', 'hiep') + ])) + + expected = """ + + + bar + + + """ + + node = etree.Element('document') + container_elm.render(node, obj) + assert_nodes_equal(expected, node) + + item = container_elm.parse(node.getchildren()[0], schema) + assert item._attr_1 == {'hiep': 'hoi', 'hoi': 'hiep'} + assert item.foo == 'bar' + + +def test_attribute_list_type(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + """)) + + container_elm = schema.get_element('{http://tests.python-zeep.org/}container') + assert container_elm.signature() == ('foo: xsd:string, lijst: xsd:int[]') + obj = container_elm(foo='bar', lijst=[1, 2, 3]) + expected = """ + + + bar + + + """ + + node = etree.Element('document') + container_elm.render(node, obj) + assert_nodes_equal(expected, node) + + item = container_elm.parse(node.getchildren()[0], schema) + assert item.lijst == [1, 2, 3] + assert item.foo == 'bar' + + +def test_ref_attribute_qualified(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + """)) + + elm_cls = schema.get_element('{http://tests.python-zeep.org/}container') + instance = elm_cls(attr="hoi") + + expected = """ + + + + """ + + node = etree.Element('document') + elm_cls.render(node, instance) + assert_nodes_equal(expected, node) + + +def test_ref_attribute_unqualified(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + """)) + + elm_cls = schema.get_element('{http://tests.python-zeep.org/}container') + instance = elm_cls(attr="hoi") + + expected = """ + + + + """ + + node = etree.Element('document') + elm_cls.render(node, instance) + assert_nodes_equal(expected, node) + + +def test_complex_type_with_attributes(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + """)) + + address_type = schema.get_element('Address') + obj = address_type( + NameFirst='John', NameLast='Doe', Email='j.doe@example.com', id='123') + + node = etree.Element('document') + address_type.render(node, obj) + + expected = """ + +
+ John + Doe + j.doe@example.com +
+
+ """ + assert_nodes_equal(expected, node) + + +def test_qualified_attribute(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + """)) + + address_type = schema.get_element('{http://tests.python-zeep.org/}Address') + obj = address_type(foo='bar', id="20", pos="30") + + expected = """ + + + bar + + + """ + + node = etree.Element('document') + address_type.render(node, obj) + assert_nodes_equal(expected, node) + + +def test_group(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + """)) + + address_type = schema.get_element('{http://tests.python-zeep.org/}Address') + obj = address_type(foo='bar', id="20", pos="30") + + expected = """ + + + bar + + + """ + + node = etree.Element('document') + address_type.render(node, obj) + assert_nodes_equal(expected, node) + + +def test_group_nested(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + + + + + + + """)) + + address_type = schema.get_element('{http://tests.python-zeep.org/}Address') + obj = address_type(foo='bar', id="20", pos="30", size="maat") + + expected = """ + + + bar + + + """ + + node = etree.Element('document') + address_type.render(node, obj) + assert_nodes_equal(expected, node) + + +def test_nested_attribute(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + + """)) + + container_elm = schema.get_element('{http://tests.python-zeep.org/}container') + assert container_elm.signature() == 'item: {x: xsd:string, y: xsd:string}' + obj = container_elm(item={'x': 'foo', 'y': 'bar'}) + + expected = """ + + + + foo + + + + """ + + node = etree.Element('document') + container_elm.render(node, obj) + assert_nodes_equal(expected, node) + + +def test_attribute_union_type(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + + + """)) + + attr = schema.get_attribute('{http://tests.python-zeep.org/}something') + assert attr('foo') == 'foo' + + +def test_attribute_union_type_inline(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + + + + """)) + + attr = schema.get_attribute('{http://tests.python-zeep.org/}something') + assert attr('foo') == 'foo' diff --git a/tests/test_xsd_builtins.py b/tests/test_xsd_builtins.py index 0d3da42..a10951d 100644 --- a/tests/test_xsd_builtins.py +++ b/tests/test_xsd_builtins.py @@ -122,6 +122,10 @@ class TestDateTime: value = datetime.datetime(2016, 3, 4, 21, 14, 42, tzinfo=pytz.utc) assert instance.xmlvalue(value) == '2016-03-04T21:14:42Z' + value = datetime.datetime(2016, 3, 4, 21, 14, 42, 123456, tzinfo=pytz.utc) + assert instance.xmlvalue(value) == '2016-03-04T21:14:42.123456Z' + + value = datetime.datetime(2016, 3, 4, 21, 14, 42, tzinfo=pytz.utc) value = value.astimezone(pytz.timezone('Europe/Amsterdam')) assert instance.xmlvalue(value) == '2016-03-04T22:14:42+01:00' @@ -130,6 +134,9 @@ class TestDateTime: value = datetime.datetime(2016, 3, 4, 21, 14, 42) assert instance.pythonvalue('2016-03-04T21:14:42') == value + value = datetime.datetime(2016, 3, 4, 21, 14, 42, 123456) + assert instance.pythonvalue('2016-03-04T21:14:42.123456') == value + def test_pythonvalue_invalid(self): instance = builtins.DateTime() with pytest.raises(ValueError): diff --git a/tests/test_xsd_choice.py b/tests/test_xsd_choice.py new file mode 100644 index 0000000..7478962 --- /dev/null +++ b/tests/test_xsd_choice.py @@ -0,0 +1,979 @@ +import pytest +from lxml import etree + +from tests.utils import assert_nodes_equal, load_xml, render_node +from zeep import xsd +from zeep.exceptions import XMLParseError +from zeep.helpers import serialize_object + + +def test_choice_element(): + node = etree.fromstring(""" + + + + + + + + + + + + + """.strip()) + schema = xsd.Schema(node) + element = schema.get_element('ns0:container') + + value = element(item_1="foo") + assert value.item_1 == 'foo' + assert value.item_2 is None + assert value.item_3 is None + + expected = """ + + + foo + + + """ + node = etree.Element('document') + element.render(node, value) + assert_nodes_equal(expected, node) + + value = element.parse(node.getchildren()[0], schema) + assert value.item_1 == 'foo' + assert value.item_2 is None + assert value.item_3 is None + + +def test_choice_element_second_elm(): + node = etree.fromstring(""" + + + + + + + + + + + + + """.strip()) + schema = xsd.Schema(node) + element = schema.get_element('ns0:container') + + value = element(item_2="foo") + assert value.item_1 is None + assert value.item_2 == 'foo' + assert value.item_3 is None + + expected = """ + + + foo + + + """ + node = etree.Element('document') + element.render(node, value) + assert_nodes_equal(expected, node) + + value = element.parse(node.getchildren()[0], schema) + assert value.item_1 is None + assert value.item_2 == 'foo' + assert value.item_3 is None + + +def test_choice_element_multiple(): + node = etree.fromstring(""" + + + + + + + + + + + + + """.strip()) + schema = xsd.Schema(node) + element = schema.get_element('ns0:container') + + value = element(_value_1=[ + {'item_1': 'foo'}, {'item_2': 'bar'}, {'item_1': 'three'}, + ]) + assert value._value_1 == [ + {'item_1': 'foo'}, {'item_2': 'bar'}, {'item_1': 'three'}, + ] + + expected = """ + + + foo + bar + three + + + """ + node = etree.Element('document') + element.render(node, value) + assert_nodes_equal(expected, node) + + value = element.parse(node.getchildren()[0], schema) + assert value._value_1 == [ + {'item_1': 'foo'}, {'item_2': 'bar'}, {'item_1': 'three'}, + ] + + +def test_choice_element_optional(): + node = etree.fromstring(""" + + + + + + + + + + + + + + + + """.strip()) + schema = xsd.Schema(node) + element = schema.get_element('ns0:container') + value = element(item_4="foo") + + expected = """ + + + foo + + + """ + node = etree.Element('document') + element.render(node, value) + assert_nodes_equal(expected, node) + + +def test_choice_element_with_any(): + node = etree.fromstring(""" + + + + + + + + + + + + + + + + + """.strip()) + schema = xsd.Schema(node) + element = schema.get_element('ns0:container') + value = element(item_1="foo", name="foo", something="bar") + + expected = """ + + + foo + + + """ + node = etree.Element('document') + element.render(node, value) + assert_nodes_equal(expected, node) + + result = element.parse(node.getchildren()[0], schema) + assert result.name == 'foo' + assert result.something is True + assert result.item_1 == 'foo' + + +def test_choice_element_with_any_max_occurs(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + + """)) + + element = schema.get_element('ns0:container') + value = element( + item_2="item-2", + _value_1=[ + xsd.AnyObject(schema.get_element('ns0:item_any'), 'any-content') + ]) + + expected = """ + + + item-2 + any-content + + + """ + node = render_node(element, value) + assert_nodes_equal(node, expected) + result = element.parse(node.getchildren()[0], schema) + assert result.item_2 == 'item-2' + assert result._value_1 == ['any-content'] + + +def test_choice_optional_values(): + schema = load_xml(""" + + + + + + + + + + """) + schema = xsd.Schema(schema) + + node = load_xml("") + elm = schema.get_type('ns0:Transport') + elm.parse_xmlelement(node, schema) + + +def test_choice_in_sequence(): + node = etree.fromstring(""" + + + + + + + + + + + + + + + + """.strip()) + schema = xsd.Schema(node) + container_elm = schema.get_element('ns0:container') + + assert container_elm.type.signature() == ( + 'something: xsd:string, ({item_1: xsd:string} | {item_2: xsd:string} | {item_3: xsd:string})') # noqa + value = container_elm(item_1='item-1') + + expected = """ + + + + item-1 + + + """ + node = etree.Element('document') + container_elm.render(node, value) + assert_nodes_equal(expected, node) + + +def test_choice_with_sequence(): + node = load_xml(""" + + + + + + + + + + + + + + + + + + """) + schema = xsd.Schema(node) + element = schema.get_element('ns0:container') + assert element.type.signature() == ( + '({item_1: xsd:string, item_2: xsd:string} | {item_3: xsd:string, item_4: xsd:string})') + value = element(item_1='foo', item_2='bar') + + expected = """ + + + foo + bar + + + """ + node = etree.Element('document') + element.render(node, value) + assert_nodes_equal(expected, node) + + +def test_choice_with_sequence_once(): + node = load_xml(""" + + + + + + + + + + + + + + + + + """) + schema = xsd.Schema(node) + element = schema.get_element('ns0:container') + assert element.type.signature() == ( + 'item_0: xsd:string, ({item_1: xsd:string, item_2: xsd:string})') + value = element(item_0='nul', item_1='foo', item_2='bar') + + expected = """ + + + nul + foo + bar + + + """ + node = etree.Element('document') + element.render(node, value) + assert_nodes_equal(expected, node) + + +def test_choice_with_sequence_once_extra_data(): + node = load_xml(""" + + + + + + + + + + + + + + + + + + """) + schema = xsd.Schema(node) + element = schema.get_element('ns0:container') + assert element.type.signature() == ( + 'item_0: xsd:string, ({item_1: xsd:string, item_2: xsd:string}), item_3: xsd:string') + value = element(item_0='nul', item_1='foo', item_2='bar', item_3='item-3') + + expected = """ + + + nul + foo + bar + item-3 + + + """ + node = etree.Element('document') + element.render(node, value) + assert_nodes_equal(expected, node) + + +def test_choice_with_sequence_second(): + node = load_xml(""" + + + + + + + + + + + + + + + + + + """) + schema = xsd.Schema(node) + element = schema.get_element('ns0:container') + assert element.type.signature() == ( + '({item_1: xsd:string, item_2: xsd:string} | {item_3: xsd:string, item_4: xsd:string})') + value = element(item_3='foo', item_4='bar') + + expected = """ + + + foo + bar + + + """ + node = etree.Element('document') + element.render(node, value) + assert_nodes_equal(expected, node) + + +def test_choice_with_sequence_invalid(): + node = load_xml(""" + + + + + + + + + + + + + + + + + + """) + schema = xsd.Schema(node) + element = schema.get_element('ns0:container') + assert element.type.signature() == ( + '({item_1: xsd:string, item_2: xsd:string} | {item_3: xsd:string, item_4: xsd:string})') + + with pytest.raises(TypeError): + element(item_1='foo', item_4='bar') + + +def test_choice_with_sequence_change(): + node = load_xml(""" + + + + + + + + + + + + + + + + + + + """) + schema = xsd.Schema(node) + element = schema.get_element('ns0:ElementName') + + elm = element(item_1='foo', item_2='bar') + assert serialize_object(elm) == { + 'item_3': None, + 'item_2': 'bar', + 'item_1': 'foo', + 'item_4': None, + 'nee': None + } + + elm.item_1 = 'bla-1' + elm.item_2 = 'bla-2' + + expected = """ + + + bla-1 + bla-2 + + + """ + node = etree.Element('document') + element.render(node, elm) + assert_nodes_equal(expected, node) + + +def test_choice_with_sequence_change_named(): + node = load_xml(""" + + + + + + + + + + + + + + + """) + schema = xsd.Schema(node) + element = schema.get_element('ns0:ElementName') + elm = element(item_3='foo') + elm = element(item_1='foo', item_2='bar') + assert elm['item_1'] == 'foo' + assert elm['item_2'] == 'bar' + + elm['item_1'] = 'bla-1' + elm['item_2'] = 'bla-2' + + expected = """ + + + bla-1 + bla-2 + + + """ + node = etree.Element('document') + element.render(node, elm) + assert_nodes_equal(expected, node) + + +def test_choice_with_sequence_multiple(): + node = load_xml(""" + + + + + + + + + + + + + + + + + + """) + schema = xsd.Schema(node) + element = schema.get_element('ns0:container') + assert element.type.signature() == ( + '({item_1: xsd:string, item_2: xsd:string} | {item_3: xsd:string, item_4: xsd:string})[]') + value = element(_value_1=[ + dict(item_1='foo', item_2='bar'), + dict(item_3='foo', item_4='bar'), + ]) + + expected = """ + + + foo + bar + foo + bar + + + """ + node = etree.Element('document') + element.render(node, value) + assert_nodes_equal(expected, node) + + +def test_choice_with_sequence_and_element(): + node = load_xml(""" + + + + + + + + + + + + + + + + + """) + schema = xsd.Schema(node) + element = schema.get_element('ns0:container') + assert element.type.signature() == ( + '({item_1: xsd:string} | {({item_2: xsd:string} | {item_3: xsd:string})})') + + value = element(item_2='foo') + + expected = """ + + + foo + + + """ + node = etree.Element('document') + element.render(node, value) + assert_nodes_equal(expected, node) + + +def test_element_ref_in_choice(): + node = etree.fromstring(""" + + + + + + + + + + + + + + + + """.strip()) + + schema = xsd.Schema(node) + + foo_type = schema.get_element('{http://tests.python-zeep.org/}foo') + assert isinstance(foo_type.type, xsd.String) + + custom_type = schema.get_element('{http://tests.python-zeep.org/}container') + + value = custom_type(foo='bar') + assert value.foo == 'bar' + assert value.bar is None + + node = etree.Element('document') + custom_type.render(node, value) + expected = """ + + + bar + + + """ + assert_nodes_equal(expected, node) + + +def test_parse_dont_loop(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + """)) + + element = schema.get_element('ns0:container') + expected = load_xml(""" + + foo + bar + foo + bar + + """) + with pytest.raises(XMLParseError): + element.parse(expected, schema) + + +def test_parse_check_unexpected(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + """)) + + element = schema.get_element('ns0:container') + expected = load_xml(""" + + foo + bar + foo + + """) + with pytest.raises(XMLParseError): + element.parse(expected, schema) + + +def test_parse_check_mixed(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + """)) + + element = schema.get_element('ns0:container') + expected = load_xml(""" + + foo + bar + foo + + """) + element.parse(expected, schema) + + +def test_parse_check_mixed_choices(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + + + + + + """)) + + element = schema.get_element('ns0:container') + + # item_1_1 + value = element(item_1_1="foo") + assert value.item_1_1 == 'foo' + + node = etree.Element('document') + element.render(node, value) + + expected = """ + + + foo + + + """ + assert_nodes_equal(expected, node) + + # item_1_2a + value = element(item_1_2a="foo") + node = etree.Element('document') + element.render(node, value) + + expected = """ + + + foo + + + """ + assert_nodes_equal(expected, node) + + # item_1_2a & item_1_2b + value = element(item_1_2a="foo", item_1_2b="bar") + node = etree.Element('document') + element.render(node, value) + + expected = """ + + + foo + bar + + + """ + assert_nodes_equal(expected, node) + + # item_2 + value = element(item_2="foo") + assert value.item_2 == 'foo' + node = etree.Element('document') + element.render(node, value) + + expected = """ + + + foo + + + """ + assert_nodes_equal(expected, node) + + # item_3 + value = element(item_3="foo") + assert value.item_3 == 'foo' + node = etree.Element('document') + element.render(node, value) + + expected = """ + + + foo + + + """ + assert_nodes_equal(expected, node) diff --git a/tests/test_xsd_complex_types.py b/tests/test_xsd_complex_types.py new file mode 100644 index 0000000..cdfbde3 --- /dev/null +++ b/tests/test_xsd_complex_types.py @@ -0,0 +1,233 @@ +import pytest + +from lxml import etree +from tests.utils import assert_nodes_equal, load_xml, render_node +from zeep import xsd + + +def test_single_node(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + """)) + schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/') + + container_elm = schema.get_element('tns:container') + obj = container_elm(item='bar') + + expected = """ + + + bar + + + """ + result = render_node(container_elm, obj) + assert_nodes_equal(result, expected) + + obj = container_elm.parse(result[0], schema) + assert obj.item == 'bar' + + +def test_nested_sequence(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + + """)) + schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/') + + container_elm = schema.get_element('tns:container') + obj = container_elm(item={'x': 1, 'y': 2}) + + expected = """ + + + + 1 + 2 + + + + """ + result = render_node(container_elm, obj) + assert_nodes_equal(result, expected) + + obj = container_elm.parse(result[0], schema) + assert obj.item.x == 1 + assert obj.item.y == 2 + + +def test_single_node_array(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + """)) + schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/') + + container_elm = schema.get_element('tns:container') + obj = container_elm(item=['item-1', 'item-2', 'item-3']) + assert obj.item == ['item-1', 'item-2', 'item-3'] + + expected = """ + + + item-1 + item-2 + item-3 + + + """ + result = render_node(container_elm, obj) + assert_nodes_equal(result, expected) + + obj = container_elm.parse(result[0], schema) + assert obj.item == ['item-1', 'item-2', 'item-3'] + + +def test_single_node_no_iterable(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + """)) + schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/') + + container_elm = schema.get_element('tns:container') + + obj = container_elm(item=['item-1', 'item-2', 'item-3']) + assert obj.item == ['item-1', 'item-2', 'item-3'] + + with pytest.raises(ValueError): + render_node(container_elm, obj) + + +def test_complex_any_types(): + # see https://github.com/mvantellingen/python-zeep/issues/252 + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + """)) + + schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/') + KeyValueData = xsd.Element( + '{http://xml.apache.org/xml-soap}KeyValueData', + xsd.ComplexType( + xsd.Sequence([ + xsd.Element( + 'key', + xsd.AnyType(), + ), + xsd.Element( + 'value', + xsd.AnyType(), + ), + ]), + ), + ) + + Map = xsd.ComplexType( + xsd.Sequence([ + xsd.Element( + 'item', + xsd.AnyType(), + min_occurs=1, + max_occurs="unbounded"), + ]), + qname=etree.QName('{http://xml.apache.org/xml-soap}Map')) + + header_Username = KeyValueData(xsd.AnyObject(xsd.String(), 'Username'), value=xsd.AnyObject(xsd.String(), 'abc')) + header_ShopId = KeyValueData(xsd.AnyObject(xsd.String(), 'ShopId'), value=xsd.AnyObject(xsd.Int(), 123)) + auth = Map(item=[header_Username, header_ShopId]) + + header_LimitNum = KeyValueData(xsd.AnyObject(xsd.String(), 'LimitNum'), value=xsd.AnyObject(xsd.Int(), 2)) + params = Map(item=[header_LimitNum]) + + container = schema.get_element('ns0:container') + obj = container(auth=auth, params=params) + + result = render_node(container, obj) + expected = load_xml(""" + + + + + Username + abc + + + ShopId + 123 + + + + + LimitNum + 2 + + + + + """) # noqa + assert_nodes_equal(result, expected) diff --git a/tests/test_xsd_extension.py b/tests/test_xsd_extension.py new file mode 100644 index 0000000..d8c5a8f --- /dev/null +++ b/tests/test_xsd_extension.py @@ -0,0 +1,597 @@ +import datetime +import io + +from lxml import etree + +from tests.utils import DummyTransport, assert_nodes_equal, load_xml, render_node +from zeep import xsd + + +def test_simple_content_extension(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + """.strip())) + shoe_type = schema.get_element('{http://tests.python-zeep.org/}ShoeSize') + + obj = shoe_type(20, sizing='EUR') + + node = render_node(shoe_type, obj) + expected = """ + + 20 + + """ + assert_nodes_equal(expected, node) + + obj = shoe_type.parse(node[0], schema) + assert obj._value_1 == 20 + assert obj.sizing == 'EUR' + + +def test_complex_content_sequence_extension(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + + + + + + """)) + address_type = schema.get_element('{http://tests.python-zeep.org/}Address') + + obj = address_type( + first_name='foo', last_name='bar', country='The Netherlands') + + node = etree.Element('document') + address_type.render(node, obj) + expected = """ + + + foo + bar + The Netherlands + + + """ + assert_nodes_equal(expected, node) + + +def test_complex_content_with_recursive_elements(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + + + + + + """)) + pet_type = schema.get_element('{http://tests.python-zeep.org/}Pet') + assert(pet_type.signature() == 'name: xsd:string, common_name: xsd:string, children: Pet') + + obj = pet_type( + name='foo', common_name='bar') + + node = etree.Element('document') + pet_type.render(node, obj) + expected = """ + + + foo + bar + + + + """ + assert_nodes_equal(expected, node) + + +def test_complex_content_sequence_extension_2(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + + + + + + """)) + elm_cls = schema.get_element('{http://tests.python-zeep.org/}container') + + node = load_xml(""" + + item-1 + item-2 + item-3 + + """) + data = elm_cls.parse(node, schema) + assert data['item-1'] == 'item-1' + assert data['item-2'] == 'item-2' + assert data['item-3'] == 'item-3' + + +def test_complex_type_with_extension_optional(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + """)) + container_elm = schema.get_element('{http://tests.python-zeep.org/}container') + obj = container_elm(main_1='foo') + + node = etree.Element('document') + container_elm.render(node, obj) + expected = """ + + + foo + + + """ + assert_nodes_equal(expected, node) + + assert_nodes_equal(expected, node) + item = container_elm.parse(node.getchildren()[0], schema) + assert item.main_1 == 'foo' + + +def test_complex_with_simple(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + """)) + address_type = schema.get_element('ns0:Address') + + assert address_type.type.signature() + val = datetime.datetime(2016, 5, 29, 11, 13, 45) + obj = address_type(val, name='foobie') + + expected = """ + + 2016-05-29T11:13:45 + + """ + node = etree.Element('document') + address_type.render(node, obj) + assert_nodes_equal(expected, node) + + +def test_sequence_with_type(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + + + + + + + + + + """)) + seq = schema.get_type('ns0:polytype') + sub_type = schema.get_type('ns0:subtype') + value = seq(item=[sub_type(attr_1='test', name='name')]) + + node = etree.Element('document') + seq.render(node, value) + + expected = """ + + + name + + + """ + assert_nodes_equal(expected, node) + + +def test_complex_simple_content(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """)) # noqa + value_elm = schema.get_element('ns0:value') + value = value_elm('00163e0c-0ea1-1ed6-93af-e818529bc1f1') + + node = etree.Element('document') + value_elm.render(node, value) + expected = """ + + 00163e0c-0ea1-1ed6-93af-e818529bc1f1 + + """ # noqa + assert_nodes_equal(expected, node) + + item = value_elm.parse(node.getchildren()[0], schema) + assert item._value_1 == '00163e0c-0ea1-1ed6-93af-e818529bc1f1' + + +def test_issue_221(): + transport = DummyTransport() + transport.bind( + 'https://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd', + load_xml(io.open('tests/wsdl_files/xmldsig-core-schema.xsd', 'r').read().encode('utf-8'))) + + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + x + + """), transport=transport) + + schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/') + elm = schema.get_element('tns:exportOrgRegistryRequest') + + # Args + obj = elm(None, {'OGRN': '123123123123', 'isRegistered': True}) + node = etree.Element('document') + elm.render(node, obj) + expected = """ + + + + 123123123123 + true + + + + """ + assert_nodes_equal(expected, node) + + obj = elm(SearchCriteria={'orgVersionGUID': '1234', 'isRegistered': False}) + node = etree.Element('document') + elm.render(node, obj) + expected = """ + + + + 1234 + false + + + + """ + assert_nodes_equal(expected, node) + + obj = elm(SearchCriteria={'OGRNIP': '123123123123', 'isRegistered': True}) + node = etree.Element('document') + elm.render(node, obj) + expected = """ + + + + 123123123123 + true + + + + """ + assert_nodes_equal(expected, node) + + +def test_complex_content_extension_with_sequence(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + """)) + address_type = schema.get_element('{http://tests.python-zeep.org/}SpecialPackage') + + obj = address_type( + id='testString', pkg_id='nameId') + + node = etree.Element('document') + address_type.render(node, obj) + expected = """ + + + + + + """ + assert_nodes_equal(expected, node) + + +def test_extension_abstract_complex_type(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + + """)) + package_cls = schema.get_element('{http://tests.python-zeep.org/}SpecialPackage') + + obj = package_cls(item='foo') + + node = etree.Element('document') + package_cls.render(node, obj) + expected = """ + + + foo + + + """ + assert_nodes_equal(expected, node) diff --git a/tests/test_xsd_integration.py b/tests/test_xsd_integration.py index 06df987..64d9847 100644 --- a/tests/test_xsd_integration.py +++ b/tests/test_xsd_integration.py @@ -1,3 +1,5 @@ +import copy + import pytest from lxml import etree @@ -125,7 +127,6 @@ def test_array(): """ node = etree.Element('document', nsmap=schema._prefix_map_custom) address_type.render(node, obj) - print(etree.tostring(node)) assert_nodes_equal(expected, node) @@ -902,3 +903,51 @@ def test_empty_xmlns(): """) item = container_elm.parse(node, schema) assert item._value_1 == 'foo' + + +def test_keep_objects_intact(): + node = etree.fromstring(""" + + + + + + + + + + + + + + + + + + + + + + + + + """.strip()) + + schema = xsd.Schema(node) + address_type = schema.get_element('{http://tests.python-zeep.org/}Address') + obj = address_type(name='foo', container={'service': [{'name': 'foo'}]}) + + org_obj = copy.deepcopy(obj) + + node = etree.Element('document') + address_type.render(node, obj) + + print(org_obj) + print(obj) + assert org_obj['container']['service'] == obj['container']['service'] diff --git a/tests/test_xsd_parse.py b/tests/test_xsd_parse.py index c7f959e..3357d7c 100644 --- a/tests/test_xsd_parse.py +++ b/tests/test_xsd_parse.py @@ -30,6 +30,33 @@ def test_sequence_parse_basic(): assert obj.item_2 == 'bar' +def test_sequence_parse_max_occurs_infinite_loop(): + custom_type = xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'authentication'), + xsd.ComplexType( + xsd.Sequence([ + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_1'), + xsd.String()), + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_2'), + xsd.String()), + ], max_occurs='unbounded') + )) + expected = etree.fromstring(""" + + foo + bar + + """) + obj = custom_type.parse(expected, None) + assert obj._value_1 == [ + { + 'item_1': 'foo', + 'item_2': 'bar', + } + ] + def test_sequence_parse_basic_with_attrs(): custom_element = xsd.Element( etree.QName('http://tests.python-zeep.org/', 'authentication'), diff --git a/tests/test_xsd_schemas.py b/tests/test_xsd_schemas.py new file mode 100644 index 0000000..269c3fe --- /dev/null +++ b/tests/test_xsd_schemas.py @@ -0,0 +1,656 @@ +import pytest +from lxml import etree + +from tests.utils import DummyTransport, load_xml +from zeep import exceptions, xsd +from zeep.xsd.builtins import Schema as Schema +from zeep.exceptions import ZeepWarning + + +def test_default_types(): + schema = xsd.Schema() + xsd_string = schema.get_type('{http://www.w3.org/2001/XMLSchema}string') + assert xsd_string == xsd.String() + + +def test_default_types_not_found(): + schema = xsd.Schema() + with pytest.raises(exceptions.LookupError): + schema.get_type('{http://www.w3.org/2001/XMLSchema}bar') + + +def test_default_elements(): + schema = xsd.Schema() + xsd_schema = schema.get_element('{http://www.w3.org/2001/XMLSchema}schema') + isinstance(xsd_schema, Schema) + + +def test_default_elements_not_found(): + schema = xsd.Schema() + with pytest.raises(exceptions.LookupError): + schema.get_element('{http://www.w3.org/2001/XMLSchema}bar') + + +def test_invalid_namespace_handling(): + schema = xsd.Schema() + qname = '{http://tests.python-zeep.org/404}foo' + + with pytest.raises(exceptions.NamespaceError) as exc: + schema.get_element(qname) + assert qname in str(exc.value.message) + + with pytest.raises(exceptions.NamespaceError) as exc: + schema.get_type(qname) + assert qname in str(exc.value.message) + + with pytest.raises(exceptions.NamespaceError) as exc: + schema.get_group(qname) + assert qname in str(exc.value.message) + + with pytest.raises(exceptions.NamespaceError) as exc: + schema.get_attribute(qname) + assert qname in str(exc.value.message) + + with pytest.raises(exceptions.NamespaceError) as exc: + schema.get_attribute_group(qname) + assert qname in str(exc.value.message) + + +def test_invalid_localname_handling(): + schema = xsd.Schema(load_xml(""" + + + + """)) + + qname = '{http://tests.python-zeep.org/}foo' + namespace = 'http://tests.python-zeep.org/' + localname = 'foo' + + with pytest.raises(exceptions.LookupError) as exc: + schema.get_element(qname) + assert namespace in str(exc.value.message) + assert localname in str(exc.value.message) + + with pytest.raises(exceptions.LookupError) as exc: + schema.get_type(qname) + assert namespace in str(exc.value.message) + assert localname in str(exc.value.message) + + with pytest.raises(exceptions.LookupError) as exc: + schema.get_group(qname) + assert namespace in str(exc.value.message) + assert localname in str(exc.value.message) + + with pytest.raises(exceptions.LookupError) as exc: + schema.get_attribute(qname) + assert namespace in str(exc.value.message) + assert localname in str(exc.value.message) + + with pytest.raises(exceptions.LookupError) as exc: + schema.get_attribute_group(qname) + assert namespace in str(exc.value.message) + assert localname in str(exc.value.message) + + +def test_schema_repr_none(): + schema = xsd.Schema() + assert repr(schema) == "')>" + + +def test_schema_repr_val(): + schema = xsd.Schema(load_xml(""" + + + + """)) + assert repr(schema) == "" + + +def test_schema_doc_repr_val(): + schema = xsd.Schema(load_xml(""" + + + + """)) + doc = schema._get_schema_document('http://tests.python-zeep.org/') + assert repr(doc) == "" + + +def test_multiple_extension(): + node_a = etree.fromstring(""" + + + + + + + + + + + + + + """.strip()) + + node_b = etree.fromstring(""" + + + + + + + + + + + + """.strip()) + + node_c = etree.fromstring(""" + + + + + + + + + + + + + + """.strip()) + etree.XMLSchema(node_c) + + transport = DummyTransport() + transport.bind('http://tests.python-zeep.org/b.xsd', node_b) + transport.bind('http://tests.python-zeep.org/c.xsd', node_c) + + schema = xsd.Schema(node_a, transport=transport) + type_a = schema.get_type('ns0:type_a') + type_a(wat='x') + + +def test_global_element_and_type(): + node_a = etree.fromstring(""" + + + + + + + + + + + + + + """.strip()) + + node_b = etree.fromstring(""" + + + + + + + + + """.strip()) + + node_c = etree.fromstring(""" + + + + + + + + + + + """.strip()) + etree.XMLSchema(node_c) + + transport = DummyTransport() + transport.bind('http://tests.python-zeep.org/b.xsd', node_b) + transport.bind('http://tests.python-zeep.org/c.xsd', node_c) + + schema = xsd.Schema(node_a, transport=transport) + type_a = schema.get_type('{http://tests.python-zeep.org/c}type_a') + + type_a = schema.get_type('{http://tests.python-zeep.org/c}type_a') + type_a(item_a='x') + + elm = schema.get_element('{http://tests.python-zeep.org/c}item') + elm('x') + + elm = schema.get_type('{http://tests.python-zeep.org/a}refs') + elm(ref_elm='foo', ref_attr='bar') + + +def test_cyclic_imports(): + schema_a = etree.fromstring(""" + + + + + + """.strip()) + + schema_b = etree.fromstring(""" + + + + + + """.strip()) + + schema_c = etree.fromstring(""" + + + + + + """.strip()) + + transport = DummyTransport() + transport.bind('http://tests.python-zeep.org/a.xsd', schema_a) + transport.bind('http://tests.python-zeep.org/b.xsd', schema_b) + transport.bind('http://tests.python-zeep.org/c.xsd', schema_c) + xsd.Schema(schema_a, transport=transport, location='http://tests.python-zeep.org/a.xsd') + + +def test_get_type_through_import(): + schema_a = etree.fromstring(""" + + + + + + + + """.strip()) + + schema_b = etree.fromstring(""" + + + + + + + """.strip()) + + transport = DummyTransport() + transport.bind('http://tests.python-zeep.org/a.xsd', schema_a) + transport.bind('http://tests.python-zeep.org/b.xsd', schema_b) + xsd.Schema(schema_a, transport=transport) + + +def test_duplicate_target_namespace(): + schema_a = etree.fromstring(""" + + + + + + + """.strip()) + + schema_b = etree.fromstring(""" + + + + """.strip()) + + schema_c = etree.fromstring(""" + + + + """.strip()) + + transport = DummyTransport() + transport.bind('http://tests.python-zeep.org/a.xsd', schema_a) + transport.bind('http://tests.python-zeep.org/b.xsd', schema_b) + transport.bind('http://tests.python-zeep.org/c.xsd', schema_c) + with pytest.warns(ZeepWarning): + xsd.Schema(schema_a, transport=transport) + + +def test_multiple_no_namespace(): + node_a = etree.fromstring(""" + + + + + + + """.strip()) + + node_b = etree.fromstring(""" + + + + """.strip()) + + transport = DummyTransport() + transport.bind('http://tests.python-zeep.org/b.xsd', node_b) + transport.bind('http://tests.python-zeep.org/c.xsd', node_b) + with pytest.warns(ZeepWarning): + xsd.Schema(node_a, transport=transport) + + +def test_multiple_only_target_ns(): + node_a = etree.fromstring(""" + + + + + + + """.strip()) + + node_b = etree.fromstring(""" + + + + """.strip()) + + transport = DummyTransport() + transport.bind('http://tests.python-zeep.org/b.xsd', node_b) + transport.bind('http://tests.python-zeep.org/c.xsd', node_b) + with pytest.warns(ZeepWarning): + xsd.Schema(node_a, transport=transport) + + +def test_schema_error_handling(): + node_a = etree.fromstring(""" + + + + + """.strip()) + transport = DummyTransport() + schema = xsd.Schema(node_a, transport=transport) + + with pytest.raises(ValueError): + schema.get_element('nonexisting:something') + with pytest.raises(ValueError): + schema.get_type('nonexisting:something') + with pytest.raises(exceptions.NamespaceError): + schema.get_element('{nonexisting}something') + with pytest.raises(exceptions.NamespaceError): + schema.get_type('{nonexisting}something') + with pytest.raises(exceptions.LookupError): + schema.get_element('ns0:something') + with pytest.raises(exceptions.LookupError): + schema.get_type('ns0:something') + + +def test_schema_import_xmlsoap(): + node_a = etree.fromstring(""" + + + + + """.strip()) + transport = DummyTransport() + xsd.Schema(node_a, transport=transport) + + +def test_schema_import_unresolved(): + node_a = etree.fromstring(""" + + + + + """.strip()) + transport = DummyTransport() + xsd.Schema(node_a, transport=transport) + + +def test_no_target_namespace(): + node_a = etree.fromstring(""" + + + + + + + + + + + + + + + """.strip()) + + node_b = etree.fromstring(""" + + + + + """.strip()) + + transport = DummyTransport() + transport.bind('http://tests.python-zeep.org/b.xsd', node_b) + xsd.Schema(node_a, transport=transport) + + +def test_include_recursion(): + node_a = etree.fromstring(""" + + + + + + + """.strip()) + + node_b = etree.fromstring(""" + + + + + + + """.strip()) + + node_c = etree.fromstring(""" + + + + + + + + """.strip()) + + transport = DummyTransport() + transport.bind('http://tests.python-zeep.org/b.xsd', node_b) + transport.bind('http://tests.python-zeep.org/c.xsd', node_c) + + schema = xsd.Schema(node_a, transport=transport) + schema.get_element('{http://tests.python-zeep.org/b}foo') + schema.get_element('{http://tests.python-zeep.org/b}bar') + + +def test_merge(): + node_a = etree.fromstring(""" + + + + + """.strip()) + + node_b = etree.fromstring(""" + + + + + """.strip()) + + schema_a = xsd.Schema(node_a) + schema_b = xsd.Schema(node_b) + schema_a.merge(schema_b) + + schema_a.get_element('{http://tests.python-zeep.org/a}foo') + schema_a.get_element('{http://tests.python-zeep.org/b}foo') diff --git a/tests/test_xsd_signatures.py b/tests/test_xsd_signatures.py new file mode 100644 index 0000000..0040d6a --- /dev/null +++ b/tests/test_xsd_signatures.py @@ -0,0 +1,187 @@ +from lxml import etree + +from zeep import xsd + + +def test_signature_complex_type_choice(): + custom_type = xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'authentication'), + xsd.ComplexType( + xsd.Choice([ + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_1'), + xsd.String()), + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_2'), + xsd.String()), + ]) + )) + assert custom_type.signature() == '({item_1: xsd:string} | {item_2: xsd:string})' + + +def test_signature_complex_type_choice_sequence(): + custom_type = xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'authentication'), + xsd.ComplexType( + xsd.Choice([ + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_1'), + xsd.String()), + xsd.Sequence([ + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_2_1'), + xsd.String()), + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_2_2'), + xsd.String()), + ]) + ]) + )) + assert custom_type.signature() == ( + '({item_1: xsd:string} | {item_2_1: xsd:string, item_2_2: xsd:string})') + + +def test_signature_nested_sequences(): + custom_type = xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'authentication'), + xsd.ComplexType( + xsd.Sequence([ + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_1'), + xsd.String()), + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_2'), + xsd.String()), + xsd.Sequence([ + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_3'), + xsd.String()), + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_4'), + xsd.String()), + ]), + xsd.Choice([ + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_5'), + xsd.String()), + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_6'), + xsd.String()), + xsd.Sequence([ + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_5'), + xsd.String()), + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_6'), + xsd.String()), + ]) + ]) + ]) + )) + + assert custom_type.signature() == ( + 'item_1: xsd:string, item_2: xsd:string, item_3: xsd:string, item_4: xsd:string, ({item_5: xsd:string} | {item_6: xsd:string} | {item_5: xsd:string, item_6: xsd:string})' # noqa + ) + + +def test_signature_nested_sequences_multiple(): + custom_type = xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'authentication'), + xsd.ComplexType( + xsd.Sequence([ + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_1'), + xsd.String()), + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_2'), + xsd.String()), + xsd.Sequence([ + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_3'), + xsd.String()), + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_4'), + xsd.String()), + ]), + xsd.Choice([ + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_5'), + xsd.String()), + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_6'), + xsd.String()), + xsd.Sequence([ + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_5'), + xsd.String()), + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_6'), + xsd.String()), + ]) + ], min_occurs=2, max_occurs=3) + ]) + )) + + assert custom_type.signature() == ( + 'item_1: xsd:string, item_2: xsd:string, item_3: xsd:string, item_4: xsd:string, _value_1: ({item_5: xsd:string} | {item_6: xsd:string} | {item_5: xsd:string, item_6: xsd:string})[]' # noqa + ) + + +def test_signature_complex_type_any(): + custom_type = xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'authentication'), + xsd.ComplexType( + xsd.Choice([ + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_1'), + xsd.String()), + xsd.Any() + ]) + )) + assert custom_type.signature() == '({item_1: xsd:string} | {_value_1: ANY})' + custom_type(item_1='foo') + + +def test_signature_complex_type_sequence_with_any(): + custom_type = xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'authentication'), + xsd.ComplexType( + xsd.Choice([ + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_1'), + xsd.String()), + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_2'), + xsd.ComplexType( + xsd.Sequence([ + xsd.Any() + ]) + ) + ) + ]) + )) + assert custom_type.signature() == ( + '({item_1: xsd:string} | {item_2: {_value_1: ANY}})') + + +def test_signature_complex_type_sequence_with_anys(): + custom_type = xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'authentication'), + xsd.ComplexType( + xsd.Choice([ + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_1'), + xsd.String()), + xsd.Element( + etree.QName('http://tests.python-zeep.org/', 'item_2'), + xsd.ComplexType( + xsd.Sequence([ + xsd.Any(), + xsd.Any(), + ]) + ) + ) + ]) + )) + assert custom_type.signature() == ( + '({item_1: xsd:string} | {item_2: {_value_1: ANY, _value_2: ANY}})') diff --git a/tests/test_xsd_simple_types.py b/tests/test_xsd_simple_types.py new file mode 100644 index 0000000..fe22a4c --- /dev/null +++ b/tests/test_xsd_simple_types.py @@ -0,0 +1,193 @@ +from lxml import etree + +from tests.utils import assert_nodes_equal, load_xml +from zeep import xsd + + +def test_simple_type(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + """)) + + item_cls = schema.get_element('{http://tests.python-zeep.org/}item') + item = item_cls(something=12345678901234567890) + + node = etree.Element('document') + item_cls.render(node, item) + expected = """ + + + 12345678901234567890 + + + """ + assert_nodes_equal(expected, node) + item = item_cls.parse(node.getchildren()[0], schema) + assert item.something == 12345678901234567890 + + +def test_simple_type_optional(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + """)) + + item_cls = schema.get_element('{http://tests.python-zeep.org/}item') + item = item_cls() + assert item.something is None + + node = etree.Element('document') + item_cls.render(node, item) + expected = """ + + + + """ + assert_nodes_equal(expected, node) + + item = item_cls.parse(node.getchildren()[0], schema) + assert item.something is None + + +def test_restriction_global(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + """)) + + type_cls = schema.get_type('{http://tests.python-zeep.org/}foo') + assert type_cls.qname.text == '{http://tests.python-zeep.org/}foo' + + +def test_restriction_anon(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + """)) + + element_cls = schema.get_element('{http://tests.python-zeep.org/}something') + assert element_cls.type.qname == etree.QName( + '{http://tests.python-zeep.org/}something') + + obj = element_cls(75) + + node = etree.Element('document') + element_cls.render(node, obj) + expected = """ + + 75 + + """ + assert_nodes_equal(expected, node) + +def test_simple_type_list(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + """)) + + element_cls = schema.get_element('{http://tests.python-zeep.org/}something') + obj = element_cls([1, 2, 3]) + assert obj == [1, 2, 3] + + node = etree.Element('document') + element_cls.render(node, obj) + expected = """ + + 1 2 3 + + """ + assert_nodes_equal(expected, node) + + +def test_simple_type_list_custom_type(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + + + """)) + + element_cls = schema.get_element('{http://tests.python-zeep.org/}something') + obj = element_cls(['Code', 'City']) + assert obj == ['Code', 'City'] + + node = etree.Element('document') + element_cls.render(node, obj) + expected = """ + + Code City + + """ + assert_nodes_equal(expected, node) diff --git a/tests/test_xsd_types.py b/tests/test_xsd_types.py new file mode 100644 index 0000000..5e902fa --- /dev/null +++ b/tests/test_xsd_types.py @@ -0,0 +1,80 @@ +import pytest +import six +from lxml import etree + +from zeep.xsd import types + + +def test_base_type(): + # Basically just for coverage... ;-) + base = types.Type() + with pytest.raises(NotImplementedError): + base.accept('x') + + with pytest.raises(NotImplementedError): + base.parse_xmlelement(None) + + with pytest.raises(NotImplementedError): + base.parsexml(None) + + with pytest.raises(NotImplementedError): + base.render(None, None) + + with pytest.raises(NotImplementedError): + base.resolve() + + base.signature() == '' + + +def test_simpletype_eq(): + type_1 = types.SimpleType() + type_2 = types.SimpleType() + + assert type_1 == type_2 + + +def test_simpletype_parse(): + node = etree.Element('foobar') + item = types.SimpleType() + + assert item.parse_xmlelement(node) is None + + +def test_simpletype_xmlvalue(): + item = types.SimpleType() + + with pytest.raises(NotImplementedError): + item.xmlvalue(None) + + +def test_simpletype_pythonvalue(): + item = types.SimpleType() + + with pytest.raises(NotImplementedError): + item.pythonvalue(None) + + +def test_simpletype_call_wrong_arg_count(): + item = types.SimpleType() + + with pytest.raises(TypeError): + item('foo', 'bar') + + +def test_simpletype_call_wrong_kwarg(): + item = types.SimpleType() + + with pytest.raises(TypeError): + item(uhhh='x') + + +def test_simpletype_str(): + item = types.SimpleType() + item.name = u'foobar' + assert six.text_type(item) == 'SimpleType(value)' + + +def test_complextype_parse_xmlelement_no_childs(): + xmlelement = etree.Element('foobar') + item = types.ComplexType() + assert item.parse_xmlelement(xmlelement, None) is None diff --git a/tests/test_xsd_union.py b/tests/test_xsd_union.py new file mode 100644 index 0000000..ad56f93 --- /dev/null +++ b/tests/test_xsd_union.py @@ -0,0 +1,91 @@ +import datetime +import io + +from lxml import etree + +from tests.utils import DummyTransport, assert_nodes_equal, load_xml, render_node +from zeep import xsd + + +def test_union_same_types(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + + """)) + + elm = schema.get_element('ns0:item') + node = render_node(elm, '102018') + expected = """ + + 102018 + + """ + assert_nodes_equal(expected, node) + value = elm.parse(node.getchildren()[0], schema) + assert value == 102018 + + +def test_union_mixed(): + schema = xsd.Schema(load_xml(""" + + + + + + + + + + + + + + + + + + """)) + + elm = schema.get_element('ns0:item') + node = render_node(elm, '102018') + expected = """ + + 102018 + + """ + assert_nodes_equal(expected, node) + value = elm.parse(node.getchildren()[0], schema) + assert value == '102018' + + node = render_node(elm, '2018') + expected = """ + + 2018 + + """ + assert_nodes_equal(expected, node) + value = elm.parse(node.getchildren()[0], schema) + assert value == '2018' diff --git a/tests/test_xsd_valueobjects.py b/tests/test_xsd_valueobjects.py new file mode 100644 index 0000000..f9c6eaa --- /dev/null +++ b/tests/test_xsd_valueobjects.py @@ -0,0 +1,403 @@ +import pytest +import six + +from zeep import xsd +from zeep.xsd import valueobjects + + +def test_simple_args(): + xsd_type = xsd.ComplexType( + xsd.Sequence([ + xsd.Element('item_1', xsd.String()), + xsd.Element('item_2', xsd.String()) + ])) + args = tuple(['value-1', 'value-2']) + kwargs = {} + result = valueobjects._process_signature(xsd_type, args, kwargs) + assert result == { + 'item_1': 'value-1', + 'item_2': 'value-2', + } + + +def test_simple_args_attributes(): + xsd_type = xsd.ComplexType( + xsd.Sequence([ + xsd.Element('item_1', xsd.String()), + xsd.Element('item_2', xsd.String()) + ]), + [ + xsd.Attribute('attr_1', xsd.String()) + ] + ) + args = tuple(['value-1', 'value-2', 'bla']) + kwargs = {} + result = valueobjects._process_signature(xsd_type, args, kwargs) + assert result == { + 'item_1': 'value-1', + 'item_2': 'value-2', + 'attr_1': 'bla', + } + + +def test_simple_args_attributes_as_kwargs(): + xsd_type = xsd.ComplexType( + xsd.Sequence([ + xsd.Element('item_1', xsd.String()), + xsd.Element('item_2', xsd.String()) + ]), + [ + xsd.Attribute('attr_1', xsd.String()) + ] + ) + args = tuple(['value-1', 'value-2']) + kwargs = {'attr_1': 'bla'} + result = valueobjects._process_signature(xsd_type, args, kwargs) + assert result == { + 'item_1': 'value-1', + 'item_2': 'value-2', + 'attr_1': 'bla', + } + + +def test_simple_args_too_many(): + xsd_type = xsd.ComplexType( + xsd.Sequence([ + xsd.Element('item_1', xsd.String()), + xsd.Element('item_2', xsd.String()) + ])) + args = tuple(['value-1', 'value-2', 'value-3']) + kwargs = {} + + try: + valueobjects._process_signature(xsd_type, args, kwargs) + except TypeError as exc: + assert six.text_type(exc) == ( + '__init__() takes at most 2 positional arguments (3 given)') + else: + assert False, "TypeError not raised" + + +def test_simple_args_too_few(): + xsd_type = xsd.ComplexType( + xsd.Sequence([ + xsd.Element('item_1', xsd.String()), + xsd.Element('item_2', xsd.String()) + ])) + args = tuple(['value-1']) + kwargs = {} + valueobjects._process_signature(xsd_type, args, kwargs) + + +def test_simple_kwargs(): + xsd_type = xsd.ComplexType( + xsd.Sequence([ + xsd.Element('item_1', xsd.String()), + xsd.Element('item_2', xsd.String()) + ])) + args = tuple([]) + kwargs = {'item_1': 'value-1', 'item_2': 'value-2'} + result = valueobjects._process_signature(xsd_type, args, kwargs) + assert result == { + 'item_1': 'value-1', + 'item_2': 'value-2', + } + + +def test_simple_mixed(): + xsd_type = xsd.ComplexType( + xsd.Sequence([ + xsd.Element('item_1', xsd.String()), + xsd.Element('item_2', xsd.String()) + ])) + args = tuple(['value-1']) + kwargs = {'item_2': 'value-2'} + result = valueobjects._process_signature(xsd_type, args, kwargs) + assert result == { + 'item_1': 'value-1', + 'item_2': 'value-2', + } + + +def test_choice(): + xsd_type = xsd.ComplexType( + xsd.Sequence([ + xsd.Choice([ + xsd.Element('item_1', xsd.String()), + xsd.Element('item_2', xsd.String()) + ]) + ]) + ) + args = tuple([]) + kwargs = {'item_2': 'value-2'} + result = valueobjects._process_signature(xsd_type, args, kwargs) + assert result == {'item_1': None, 'item_2': 'value-2'} + + +def test_choice_max_occurs_simple_interface(): + fields = xsd.ComplexType( + xsd.Sequence([ + xsd.Choice([ + xsd.Element('item_1', xsd.String()), + xsd.Element('item_2', xsd.String()) + ], max_occurs=2) + ]) + ) + args = tuple([]) + kwargs = { + '_value_1': [{'item_1': 'foo'}, {'item_2': 'bar'}] + } + result = valueobjects._process_signature(fields, args, kwargs) + assert result == { + '_value_1': [ + {'item_1': 'foo'}, + {'item_2': 'bar'}, + ] + } + + +def test_choice_max_occurs(): + fields = xsd.ComplexType( + xsd.Sequence([ + xsd.Choice([ + xsd.Element('item_1', xsd.String()), + xsd.Element('item_2', xsd.String()) + ], max_occurs=3) + ]) + ) + args = tuple([]) + kwargs = { + '_value_1': [ + {'item_1': 'foo'}, {'item_2': 'bar'}, {'item_1': 'bla'} + ] + } + result = valueobjects._process_signature(fields, args, kwargs) + assert result == { + '_value_1': [ + {'item_1': 'foo'}, + {'item_2': 'bar'}, + {'item_1': 'bla'}, + ] + } + + +def test_choice_max_occurs_on_choice(): + xsd_type = xsd.ComplexType( + xsd.Sequence([ + xsd.Choice([ + xsd.Element('item_1', xsd.String(), max_occurs=2), + xsd.Element('item_2', xsd.String()) + ], max_occurs=2) + ]) + ) + args = tuple([]) + kwargs = { + '_value_1': [ + {'item_1': ['foo', 'bar']}, + {'item_2': 'bla'}, + ] + } + result = valueobjects._process_signature(xsd_type, args, kwargs) + assert result == { + '_value_1': [ + {'item_1': ['foo', 'bar']}, + {'item_2': 'bla'} + ] + } + + +def test_choice_mixed(): + xsd_type = xsd.ComplexType( + xsd.Sequence([ + xsd.Choice([ + xsd.Element('item_1', xsd.String()), + xsd.Element('item_2', xsd.String()), + ]), + xsd.Element('item_2', xsd.String()) + ]) + ) + expected = '({item_1: xsd:string} | {item_2: xsd:string}), item_2__1: xsd:string' + assert xsd_type.signature() == expected + + args = tuple([]) + kwargs = {'item_1': 'value-1', 'item_2__1': 'value-2'} + result = valueobjects._process_signature(xsd_type, args, kwargs) + assert result == { + 'item_1': 'value-1', + 'item_2': None, + 'item_2__1': 'value-2', + } + + +def test_choice_sequences_simple(): + xsd_type = xsd.ComplexType( + xsd.Sequence([ + xsd.Choice([ + xsd.Sequence([ + xsd.Element('item_1', xsd.String()), + xsd.Element('item_2', xsd.String()) + ]), + xsd.Sequence([ + xsd.Element('item_3', xsd.String()), + xsd.Element('item_4', xsd.String()) + ]), + ]) + ]) + ) + args = tuple([]) + kwargs = {'item_1': 'value-1', 'item_2': 'value-2'} + result = valueobjects._process_signature(xsd_type, args, kwargs) + assert result == { + 'item_1': 'value-1', + 'item_2': 'value-2', + 'item_3': None, + 'item_4': None, + } + + +def test_choice_sequences_no_match(): + xsd_type = xsd.ComplexType( + xsd.Sequence([ + xsd.Choice([ + xsd.Sequence([ + xsd.Element('item_1', xsd.String()), + xsd.Element('item_2', xsd.String()) + ]), + xsd.Sequence([ + xsd.Element('item_3', xsd.String()), + xsd.Element('item_4', xsd.String()) + ]), + ]) + ]) + ) + args = tuple([]) + with pytest.raises(TypeError): + kwargs = {'item_1': 'value-1', 'item_3': 'value-3'} + valueobjects._process_signature(xsd_type, args, kwargs) + + +def test_choice_sequences_no_match_last(): + xsd_type = xsd.ComplexType( + xsd.Sequence([ + xsd.Choice([ + xsd.Sequence([ + xsd.Element('item_1', xsd.String()), + xsd.Element('item_2', xsd.String()) + ]), + xsd.Sequence([ + xsd.Element('item_3', xsd.String()), + xsd.Element('item_4', xsd.String()) + ]), + ]) + ]) + ) + args = tuple([]) + with pytest.raises(TypeError): + kwargs = {'item_2': 'value-2', 'item_4': 'value-4'} + valueobjects._process_signature(xsd_type, args, kwargs) + + +def test_choice_sequences_no_match_nested(): + xsd_type = xsd.ComplexType( + xsd.Sequence([ + xsd.Choice([ + xsd.Sequence([ + xsd.Element('item_1', xsd.String()), + xsd.Element('item_2', xsd.String()) + ]), + ]) + ]) + ) + args = tuple([]) + kwargs = {'item_1': 'value-1'} + value = valueobjects._process_signature(xsd_type, args, kwargs) + assert value == { + 'item_1': 'value-1', + 'item_2': None, + } + + +def test_choice_sequences_optional_elms(): + xsd_type = xsd.ComplexType( + xsd.Sequence([ + xsd.Choice([ + xsd.Sequence([ + xsd.Element('item_1', xsd.String()), + xsd.Element('item_2', xsd.String(), min_occurs=0) + ]), + xsd.Sequence([ + xsd.Element('item_1', xsd.String()), + xsd.Element('item_2', xsd.String()), + xsd.Element('item_3', xsd.String()) + ]), + ]) + ]) + ) + args = tuple([]) + kwargs = {'item_1': 'value-1'} + result = valueobjects._process_signature(xsd_type, args, kwargs) + assert result == { + 'item_1': 'value-1', + 'item_2': None, + 'item_3': None, + } + + +def test_choice_sequences_max_occur(): + xsd_type = xsd.ComplexType( + xsd.Sequence([ + xsd.Choice([ + xsd.Sequence([ + xsd.Element('item_1', xsd.String()), + xsd.Element('item_2', xsd.String()) + ]), + xsd.Sequence([ + xsd.Element('item_2', xsd.String()), + xsd.Element('item_3', xsd.String()), + ]), + ], max_occurs=2) + ]), + ) + args = tuple([]) + kwargs = { + '_value_1': [ + {'item_1': 'value-1', 'item_2': 'value-2'}, + {'item_2': 'value-2', 'item_3': 'value-3'}, + ] + } + + result = valueobjects._process_signature(xsd_type, args, kwargs) + assert result == { + '_value_1': [ + {'item_1': 'value-1', 'item_2': 'value-2'}, + {'item_2': 'value-2', 'item_3': 'value-3'}, + ] + } + + +def test_choice_sequences_init_dict(): + xsd_type = xsd.ComplexType( + xsd.Sequence([ + xsd.Choice([ + xsd.Sequence([ + xsd.Element('item_1', xsd.String()), + xsd.Element('item_2', xsd.String()) + ]), + xsd.Sequence([ + xsd.Element('item_2', xsd.String()), + xsd.Element('item_3', xsd.String()), + ]), + ], max_occurs=2) + ]), + ) + args = tuple([]) + kwargs = { + '_value_1': {'item_1': 'value-1', 'item_2': 'value-2'}, + } + + result = valueobjects._process_signature(xsd_type, args, kwargs) + assert result == { + '_value_1': [ + {'item_1': 'value-1', 'item_2': 'value-2'} + ] + } diff --git a/tests/wsdl_files/soap-enc.xsd b/tests/wsdl_files/soap-enc.xsd new file mode 100644 index 0000000..de4a44a --- /dev/null +++ b/tests/wsdl_files/soap-enc.xsd @@ -0,0 +1,535 @@ + + + + + + + + + 'root' can be used to distinguish serialization roots from other + elements that are present in a serialization but are not roots of + a serialized value graph + + + + + + + + + + + + + Attributes common to all elements that function as accessors or + represent independent (multi-ref) values. The href attribute is + intended to be used in a manner like CONREF. That is, the element + content should be empty iff the href attribute appears + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 'Array' is a complex type for accessors identified by position + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/wsdl_files/soap_import_2.wsdl b/tests/wsdl_files/soap_import_2.wsdl new file mode 100644 index 0000000..be02d41 --- /dev/null +++ b/tests/wsdl_files/soap_import_2.wsdl @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/wsdl_files/soap_import_main.wsdl b/tests/wsdl_files/soap_import_main.wsdl new file mode 100644 index 0000000..976c4cf --- /dev/null +++ b/tests/wsdl_files/soap_import_main.wsdl @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + My first service + + + + + + diff --git a/tests/wsdl_files/soap_transport_err.wsdl b/tests/wsdl_files/soap_transport_err.wsdl new file mode 100644 index 0000000..980f632 --- /dev/null +++ b/tests/wsdl_files/soap_transport_err.wsdl @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + My first service + + + + + diff --git a/tests/wsdl_files/test_import_2.xsd b/tests/wsdl_files/test_import_2.xsd new file mode 100644 index 0000000..90194f6 --- /dev/null +++ b/tests/wsdl_files/test_import_2.xsd @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/wsdl_files/xmldsig-core-schema.xsd b/tests/wsdl_files/xmldsig-core-schema.xsd new file mode 100644 index 0000000..dd5254b --- /dev/null +++ b/tests/wsdl_files/xmldsig-core-schema.xsd @@ -0,0 +1,309 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tox.ini b/tox.ini deleted file mode 100644 index be542d5..0000000 --- a/tox.ini +++ /dev/null @@ -1,7 +0,0 @@ -[tox] -envlist = py27,py33,py34,py35,pypy - -[testenv] -commands = - pip install .[test] - py.test -vvv