diff --git a/MANIFEST.in b/MANIFEST.in index 016e839d..545dc25e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -35,7 +35,7 @@ exclude doc/*.eap prune doc/W3C # Examples that didn't go into bundles -recursive-include examples *.txt *.py *.sh *.xsd *.xml *-patch *.out *.xhtml +recursive-include examples *.txt *.py *.sh *.xsd *.xml *-patch *.out *.expected *.xhtml include examples/unicode_jp/pyxbgen_jp # Detritus diff --git a/README.txt b/README.txt index 8a80b791..39c2c4d2 100644 --- a/README.txt +++ b/README.txt @@ -1,5 +1,5 @@ PyXB -- Python W3C XML Schema Bindings -Version 1.2.5 +Version 1.2.6 The source releases includes pre-built bundles for common XML namespaces, assorted web service namespaces, and SAML. A bundle with over 75 namespaces @@ -8,7 +8,7 @@ those, read pyxb/bundles/opengis/README.txt before installing PyXB. Installation: python setup.py install -Documentation: doc/html +Documentation: doc/html or https://pabigot.github.io/pyxb/ Help Forum: http://sourceforge.net/forum/forum.php?forum_id=956708 diff --git a/README.txt.in b/README.txt.in index 7447e05d..d3516c56 100644 --- a/README.txt.in +++ b/README.txt.in @@ -8,7 +8,7 @@ those, read pyxb/bundles/opengis/README.txt before installing PyXB. Installation: python setup.py install -Documentation: doc/html +Documentation: doc/html or https://pabigot.github.io/pyxb/ Help Forum: http://sourceforge.net/forum/forum.php?forum_id=956708 diff --git a/doc/Makefile b/doc/Makefile index 2b1b3fd3..da686f7e 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -6,16 +6,12 @@ SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = -EPYDOC = epydoc -EPYDOCOPTS = --config documentation.cfg - # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest -.PHONY: apihtml help: @echo "Please use \`make ' where is one of" @@ -30,19 +26,15 @@ help: @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" -apihtml: - mkdir -p html/api - PYTHONPATH=.. $(EPYDOC) $(EPYDOCOPTS) - pyxbgen_cli.txt: ../pyxb/binding/generate.py ../maintainer/gendoc PYTHONPATH=.. ../maintainer/gendoc \ - | sed -e 's@ *$$@@' \ + | sed -e 's@ *$$@@' -e '$${/^$$/d;}' \ > $@ GENERATED_TXT += pyxbgen_cli.txt html: $(GENERATED_TXT) - PYTHONPATH=. $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) html + PYTHONPATH=.:.. $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) html @echo @echo "Build finished. The HTML pages are in _build/html." diff --git a/doc/arch_binding.txt b/doc/arch_binding.txt index 7eeff030..77bd404c 100644 --- a/doc/arch_binding.txt +++ b/doc/arch_binding.txt @@ -19,27 +19,27 @@ Supporting Capabilities Common Binding Instance Features ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -:api:`pyxb.binding.basis._TypeBinding_mixin` is a marker class to indicate +:py:obj:`pyxb.binding.basis._TypeBinding_mixin` is a marker class to indicate that the incorporating class represents a binding associated with a type definition (whether simple or complex). The key features of this mixin are: - - The :api:`_ExpandedName + - The :py:obj:`_ExpandedName ` class variable is overridden in each class to identify the type definition corresponding to the class. - - The :api:`_XSDLocation + - The :py:obj:`_XSDLocation ` class variable is overridden in each class to provide the line, column, and schema at which the data type definition was found. This is used in diagnostics. - - The :api:`namespace context + - The :py:obj:`namespace context ` of the type definition is recorded to allow users to perform QName resolution of values within instance documents (see, for example, the customized bindings in ``pyxb.bundles.wssplat.wsdl11``). - - Each instance records the :api:`pyxb.binding.basis.element` instance that + - Each instance records the :py:obj:`pyxb.binding.basis.element` instance that determines where the type came from. The element is required in order to provide the correct name when converting the binding instance to a DOM instance on its way to expression as a text XML document. @@ -48,7 +48,7 @@ definition (whether simple or complex). The key features of this mixin are: `_ information for the instance is stored. - - A :api:`Factory ` infrastructure is + - A :py:obj:`Factory ` infrastructure is provided to allow creation of new instances of the binding while permitting developers to customize the generated binding classes; see :ref:`binding_customization`. @@ -70,38 +70,38 @@ their XML name, the chances of conflict are high. PyXB resolves this by ensuring every identifiable object has a unique identifier within its context. The steps involved are: -#. Make object name into an :api:`identifier +#. Make object name into an :py:obj:`identifier ` by stripping out non-printable characters, replacing characters that cannot appear in identifiers with underscores, stripping leading underscores, and prefixing an initial digit with the character ``n``. -#. :api:`Deconflict ` the resulting - identifier from Python :api:`reserved identifiers +#. :py:obj:`Deconflict ` the resulting + identifier from Python :py:obj:`reserved identifiers ` and other context-specific keywords. #. Prepend the standard prefix that denotes the identifier's :ref:`visibility ` (public, protected, private) -#. Make the resulting identifier :api:`unique ` +#. Make the resulting identifier :py:obj:`unique ` within its context (containing class or module). These steps are encapsulated into a single function -:api:`pyxb.utils.utility.PrepareIdentifier` which takes parameters that +:py:obj:`pyxb.utils.utility.PrepareIdentifier` which takes parameters that customize the context for the identifier. In addition to name conflicts with namespace-global identifiers appearing directly in the module, conflicts may also appear within a binding class as a result of collision with names from Python keywords, public class names, and public field or method names in the class. The -:api:`pyxb.utils.utility._DeconflictSymbols_mixin` is used to refine the set +:py:obj:`pyxb.utils.utility._DeconflictSymbols_mixin` is used to refine the set of type-specific public names. If you customize a generated binding class by extending from it, you must specify your own class variable ``_ReservedSymbols`` with a value that is the union of your symbols and those -of the superclass(es) (see :api:`pyxb.utils.utility._DeconflictSymbols_mixin` +of the superclass(es) (see :py:obj:`pyxb.utils.utility._DeconflictSymbols_mixin` for details). -Deconfliction of module-level names occurs prior to :api:`code generation +Deconfliction of module-level names occurs prior to :py:obj:`code generation `. Identifiers are deconflicted in favor of higher items on this list: @@ -122,9 +122,9 @@ the generated bindings by adding both functionality and derived content. Maintenance issues require that these extensions exist separately from the automatically-generated binding modules; usability requires that they inherit from the automatically-generated modules. This is supported by the -:api:`pyxb.binding.basis._DynamicCreate_mixin` class. +:py:obj:`pyxb.binding.basis._DynamicCreate_mixin` class. -This class provides a :api:`method +This class provides a :py:obj:`method ` which is used by the generated bindings to create new instances of themselves. The raw bindings are generated into a sub-module with the prefix ``raw``, and the @@ -159,10 +159,10 @@ Simple Type Definitions `Simple type definitions `_ derive from -:api:`pyxb.binding.basis.simpleTypeDefinition` and a standard Python type. +:py:obj:`pyxb.binding.basis.simpleTypeDefinition` and a standard Python type. For simple types that are not derived by list or union, you can construct -instances using the :api:`Factory ` +instances using the :py:obj:`Factory ` method or directly, providing the value as an argument. New instance creation is validated against the `facets `_ recorded in the binding class. @@ -175,7 +175,7 @@ Each class corresponding to a simple type definition has class variables for the `constraining facets `_ that are valid for that class. These variables are named by prefixing the facet name with ``_CF_``, and have a value that is an instance of the -corresponding :api:`facet class `. Where possible, the +corresponding :py:obj:`facet class `. Where possible, the variables are inherited from the parent class; when a simple type is derived by restriction, the restricted class overrides its parent with a new value for the corresponding facet. @@ -199,11 +199,11 @@ Enumeration and pattern constraints maintain a list of the respective acceptable enumeration and pattern values. Facets implement the -:api:`pyxb.binding.facets.ConstrainingFacet.validateConstraint` method, which +:py:obj:`pyxb.binding.facets.ConstrainingFacet.validateConstraint` method, which in turn is invoked by the -:api:`pyxb.binding.basis.simpleTypeDefinition.XsdConstraintsOK` class method +:py:obj:`pyxb.binding.basis.simpleTypeDefinition.XsdConstraintsOK` class method when given a value that may or may not satisfy the constraints. The -:api:`Factory ` will normally +:py:obj:`Factory ` will normally validate the constraints before allowing a new instance to be returned. List Types @@ -211,9 +211,9 @@ List Types Simple types that derive by `list `_ extend from -:api:`pyxb.binding.basis.STD_list` which in turn descends from the Python +:py:obj:`pyxb.binding.basis.STD_list` which in turn descends from the Python ``list`` type. These derived classes must override the base class -:api:`pyxb.binding.basis.STD_list._ItemType` value with the appropriate +:py:obj:`pyxb.binding.basis.STD_list._ItemType` value with the appropriate class to use when creating or validating list members. When constructing an instance of a simple list type, you can provide a list as @@ -224,9 +224,9 @@ Union Types ^^^^^^^^^^^ Union types are classes that are never instantiated. Instead, the binding -classes define a :api:`pyxb.binding.basis.STD_union._MemberTypes` variable +classes define a :py:obj:`pyxb.binding.basis.STD_union._MemberTypes` variable which contains a list of binding classes that are permitted as members of the -union. The :api:`pyxb.binding.basis.STD_union.Factory` method attempts, in +union. The :py:obj:`pyxb.binding.basis.STD_union.Factory` method attempts, in turn, to create an instance of each potential member type using the arguments passed into it. The returned value is the first instance that was successfully created. @@ -236,7 +236,7 @@ member of a union is not recorded in the attribute value. See :ref:`attributeUse`. It is not possible to construct an instance of a union type directly. You -must use the :api:`Factory ` method, +must use the :py:obj:`Factory ` method, with an argument that is acceptable as an initializer for one of the member types. @@ -246,24 +246,24 @@ Complex Type Definitions `Complex type definitions `_ derive from -:api:`pyxb.binding.basis.complexTypeDefinition`. Classes representing complex -type definitions record maps that specify :api:`attribute -` and :api:`element +:py:obj:`pyxb.binding.basis.complexTypeDefinition`. Classes representing complex +type definitions record maps that specify :py:obj:`attribute +` and :py:obj:`element ` use structures to record the attributes and elements that comprise the type. Each such structure is stored as a private class field and is used by Python properties to provide access to element and attribute values in a binding instance. -The base :api:`pyxb.binding.basis.complexTypeDefinition` class provides the +The base :py:obj:`pyxb.binding.basis.complexTypeDefinition` class provides the infrastructure to identify the appropriate attribute or element given an XML tag name. For classes corresponding to types that enable `wildcards `_, it also provides a mechanism -to access unrecognized elements and attributes. :api:`Wildcard elements +to access unrecognized elements and attributes. :py:obj:`Wildcard elements ` are converted to binding instances if their namespace and name are recognized, and otherwise -are left as DOM nodes. :api:`Wildcard attributes +are left as DOM nodes. :py:obj:`Wildcard attributes ` are stored in -a map from their :api:`expanded name ` to the +a map from their :py:obj:`expanded name ` to the unicode value of the attribute. When creating a complex type definition, you can provide the values for its @@ -301,8 +301,8 @@ Simple Content Complex types with simple content (i.e., those in which the body of the element is an octet sequence in the lexical space of a specified simple type) are distinguished by providing a value for the class-level -:api:`pyxb.binding.basis.complexTypeDefinition._TypeDefinition` variable. -For these types, the :api:`pyxb.binding.basis.complexTypeDefinition.content` +:py:obj:`pyxb.binding.basis.complexTypeDefinition._TypeDefinition` variable. +For these types, the :py:obj:`pyxb.binding.basis.complexTypeDefinition.content` method returns the instance of that type that corresponds to the content of the object. @@ -335,17 +335,16 @@ was not necessary for ``v.simple`` or ``v.attribute``. Mixed and Element-Only Content ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -`Mixed and element-only content -`_ nodes use the :api:` -` method +`Mixed and element-only content `_ nodes +use the :py:obj:`pyxb.binding.basis.complexTypeDefinition._ElementBindingDeclForName` method to manage the mapping from XML schema element names to class members. The element and attribute names are distinct. Instances of complex types also -reference a :api:`content automaton +reference a :py:obj:`content automaton ` to ensure the constraints of the schema are satisfied. These structures are described in :ref:`contentModel`. -For these types, the :api:`pyxb.binding.basis.complexTypeDefinition.content` +For these types, the :py:obj:`pyxb.binding.basis.complexTypeDefinition.content` method returns a list, in parsed order, of the Python objects and (if mixed) non-element content that belong to the instance. Be aware that this order currently does not influence the order of elements when converting bindings @@ -356,7 +355,7 @@ Elements Unlike the bindings for schema type definitions, which are represented as Python classes, bindings corresponding to XML Schema element declarations are -represented as instances of the :api:`pyxb.binding.basis.element` class. The +represented as instances of the :py:obj:`pyxb.binding.basis.element` class. The instances can be used to create new binding instances that are associated with the element. Elements are used in the content model to identify transitions through a finite automaton. diff --git a/doc/arch_component.txt b/doc/arch_component.txt index cee19eb2..ea87bae5 100644 --- a/doc/arch_component.txt +++ b/doc/arch_component.txt @@ -8,7 +8,7 @@ complex relation of data objects. Each object class corresponds to one of the thirteen `XML Schema Components `_, and names of components and their properties follow those definitions. All classes specific to the -component model are found in the :api:`pyxb.xmlschema.structures` module. +component model are found in the :py:obj:`pyxb.xmlschema.structures` module. The relationships amongst components is depicted in the following diagram. Composite aggregation generally denotes ownership and shared aggregation @@ -22,36 +22,36 @@ Component Model Mix-ins A variety of :ref:`mixins` are used to allow common code to be abstracted or to mark objects as having certain capabilities. These mixins are: -- :api:`pyxb.xmlschema.structures._SchemaComponent_mixin` marks the object +- :py:obj:`pyxb.xmlschema.structures._SchemaComponent_mixin` marks the object as being a schema component and holds its - :api:`pyxb.namespace.NamespaceContext`. It also records relations between + :py:obj:`pyxb.namespace.NamespaceContext`. It also records relations between components in a global definition and their clones where those definitions are expanded. -- :api:`pyxb.xmlschema.structures._Singleton_mixin` is used to ensure there is only one instance each +- :py:obj:`pyxb.xmlschema.structures._Singleton_mixin` is used to ensure there is only one instance each of the `simple ur-type `_ and `ur-type `_. It overloads ``__new__`` to ensure that only one instance of the class is ever constructed. -- :api:`pyxb.xmlschema.structures._Annotated_mixin` provides the support for all components that +- :py:obj:`pyxb.xmlschema.structures._Annotated_mixin` provides the support for all components that contain an `annotation `_ as a child element. -- :api:`pyxb.xmlschema.structures._NamedComponent_mixin` supports components +- :py:obj:`pyxb.xmlschema.structures._NamedComponent_mixin` supports components that can be identified by name. This includes the target namespace (which may be anonymous) if the component is global, and the `complex type definition `_ that serves as the component's `scope `_ when it is local. -- :api:`pyxb.xmlschema.structures._ValueConstraint_mixin` provides support +- :py:obj:`pyxb.xmlschema.structures._ValueConstraint_mixin` provides support for components that have `value constraints `_ : that is, provide a default value and optionally require a fixed value. -- :api:`pyxb.xmlschema.structures._ScopedDeclaration_mixin` is used by +- :py:obj:`pyxb.xmlschema.structures._ScopedDeclaration_mixin` is used by `element declarations `_ and `complex type definitions @@ -59,7 +59,7 @@ to mark objects as having certain capabilities. These mixins are: named but only referenceable within a specific `scope `_. -- :api:`pyxb.xmlschema.structures._AttributeWildcard_mixin` provides support +- :py:obj:`pyxb.xmlschema.structures._AttributeWildcard_mixin` provides support for `attribute group definitions `_ and `complex type definitions @@ -72,7 +72,7 @@ Other Information Most of the important information about the component model is best obtained from the `specification `_ or -from the :api:`PyXB component model API `. +from the :py:obj:`PyXB component model API `. Tidbits of other relevant information: - An understanding of :ref:`resolution` is important. diff --git a/doc/arch_content.txt b/doc/arch_content.txt index 16098ee7..328046d9 100644 --- a/doc/arch_content.txt +++ b/doc/arch_content.txt @@ -19,7 +19,7 @@ Associating XML and Python Objects ---------------------------------- Most of the classes involved in the content model are in the -:api:`pyxb.binding.content` module. The relations among these classes are +:py:obj:`pyxb.binding.content` module. The relations among these classes are displayed in the following diagram. .. _cd_contentModel: @@ -34,12 +34,12 @@ distinct names in the Python class corresponding to that type. Use information for each of these is maintained in the type class. This use information comprises: -- the original :api:`name ` of the element/attribute in the XML -- its :api:`deconflicted name ` in Python +- the original :py:obj:`name ` of the element/attribute in the XML +- its :py:obj:`deconflicted name ` in Python - the private name by which the value is stored in the Python instance dictionary Other information is specific to the type of use. The -:api:`pyxb.binding.basis.complexTypeDefinition` retains maps from the +:py:obj:`pyxb.binding.basis.complexTypeDefinition` retains maps from the component's name the attribute use or element use instance corresponding to the component's use. @@ -50,30 +50,30 @@ Attribute Uses The information associated with an `attribute use `_ is recorded in an -:api:`pyxb.binding.content.AttributeUse` instance. This class provides: +:py:obj:`pyxb.binding.content.AttributeUse` instance. This class provides: -- The :api:`name ` of the +- The :py:obj:`name ` of the attribute -- The :api:`default value ` of +- The :py:obj:`default value ` of the attribute -- Whether the attribute value is :api:`fixed ` +- Whether the attribute value is :py:obj:`fixed ` - Whether the `attribute use `_ is - :api:`required ` - or :api:`prohibited ` + :py:obj:`required ` + or :py:obj:`prohibited ` -- The :api:`type ` of the - attribute, as a subclass of :api:`pyxb.binding.basis.simpleTypeDefinition` +- The :py:obj:`type ` of the + attribute, as a subclass of :py:obj:`pyxb.binding.basis.simpleTypeDefinition` -- Methods to :api:`read `, :api:`set - `, and :api:`reset +- Methods to :py:obj:`read `, :py:obj:`set + `, and :py:obj:`reset ` the value of the attribute in a given binding instance. -A :api:`map ` is used +A :py:obj:`map ` is used to map from expanded names to AttributeUse instances. This map is defined within the class definition itself. @@ -85,34 +85,34 @@ Element Uses The element analog to an attribute use is an `element declaration `_, and the corresponding information is stored in a -:api:`pyxb.binding.content.ElementDeclaration` instance. This class provides: +:py:obj:`pyxb.binding.content.ElementDeclaration` instance. This class provides: -- The :api:`element binding ` +- The :py:obj:`element binding ` that defines the properties of the referenced element, including its type -- Whether the use allows :api:`multiple occurrences +- Whether the use allows :py:obj:`multiple occurrences ` -- The :api:`default value ` of +- The :py:obj:`default value ` of the element. Currently this is either C{None} or an empty list, depending - on :api:`pyxb.binding.content.ElementDeclaration.isPlural` + on :py:obj:`pyxb.binding.content.ElementDeclaration.isPlural` -- Methods to :api:`read `, :api:`set - `, :api:`append to +- Methods to :py:obj:`read `, :py:obj:`set + `, :py:obj:`append to ` (only for plural elements), and - :api:`reset ` the value of the + :py:obj:`reset ` the value of the element in a given binding instance -- The :api:`setOrAppend ` method, +- The :py:obj:`setOrAppend ` method, which is most commonly used to provide new content to a value -A :api:`map ` is used to +A :py:obj:`map ` is used to map from expanded names to ElementDeclaration instances. This map is defined within the class definition itself. As mentioned before, when the same element name appears at multiple places within the element content the uses are collapsed into a single attribute on the complex type; thus the map is to -the :api:`ElementDeclaration `, not -the :api:`ElementUse `. +the :py:obj:`ElementDeclaration `, not +the :py:obj:`ElementUse `. .. _validating-content: @@ -132,8 +132,8 @@ without the complexity of the original back-tracking validation solution from :ticket:`incorrect rejection of valid documents <112>` that (rarely) occurred with the greedy algorithm introduced in :ref:`PyXB 1.1.2 `. Conversion to this data structure also enabled the distinction between -:api:`element declaration ` and -:api:`element use ` nodes, allowing +:py:obj:`element declaration ` and +:py:obj:`element use ` nodes, allowing diagnostics to trace back to the element references in context. The data structures for the automaton and the configuration structure @@ -150,7 +150,7 @@ that maintains a distinct sub-automaton for each alternative, requiring a layered approach where executon of an automaton is suspended until the subordinate automaton has accepted and a transition out of it is encountered. -For more information on the implementation, please see the :api:`FAC module +For more information on the implementation, please see the :py:obj:`FAC module `. This module has been written to be independent of PyXB infrastructure, and may be re-used in other code in accordance with the :ref:`PyXB license `. @@ -159,21 +159,21 @@ FAC and the PyXB Content Model ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ As depicted in the :ref:`Content Model class diagram ` each -complex type binding class has a :api:`_Automaton ` +complex type binding class has a :py:obj:`_Automaton ` which encodes the content model of the type as a Finite Automaton with Counters. This representation models the occurrence constraints and sub-element orders, referencing the specific element and wildcard uses as they appear in the schema. Each instance of a complex binding supports an -:api:`AutomatonConfiguration ` +:py:obj:`AutomatonConfiguration ` that is used to validate the binding content against the model. -An :api:`ElementUse ` instance is provided as +An :py:obj:`ElementUse ` instance is provided as the metadata for automaton states that correspond an element declaration in the -schema. Similarly, a :api:`WildcardUse ` +schema. Similarly, a :py:obj:`WildcardUse ` instance is used as the metadata for automaton states that correspond to an instance of the `xs:any `_ wildcard schema component. Validation in the automaton delegates through the -:api:`SymbolMatch_mixin ` interface to see +:py:obj:`SymbolMatch_mixin ` interface to see whether content in the form of a complex type binding instance is conformant to the restrictions on symbols associated with a particular state. @@ -181,13 +181,13 @@ When parsing, a transition taken results in the storage of the consumed symbol into the appropriate element attribute or wildcard list in the binding instance. In many cases, the transition from one state to a next is uniquely determined by the content; as long as this condition holds, the -:api:`AutomatonConfiguration ` -instance retains a single underlying :api:`FAC Configuration +:py:obj:`AutomatonConfiguration ` +instance retains a single underlying :py:obj:`FAC Configuration ` representing the current state. To generate the XML corresponding to a binding instance, the element and wildcard content of the instance are loaded into a Python dictionary, keyed by -the :api:`ElementDeclaration `. +the :py:obj:`ElementDeclaration `. These subordinate elements are appended to a list of child nodes as transitions that recognize them are encountered. As of :ref:`PyXB 1.2.0 ` the first legal transition in the order imposed by the schema is diff --git a/doc/arch_namespaces.txt b/doc/arch_namespaces.txt index 8d807854..e2d4fa81 100644 --- a/doc/arch_namespaces.txt +++ b/doc/arch_namespaces.txt @@ -24,12 +24,12 @@ the same namespace in different instance documents, as in the `dangling type `_ pattern. This diagram shows the class structure of the PyXB namespace infrastructure. -The central object is the :api:`pyxb.namespace.Namespace`. Four mix-in +The central object is the :py:obj:`pyxb.namespace.Namespace`. Four mix-in classes provide implementations of separate namespace functions. -:api:`pyxb.namespace.ExpandedName` is used ubiquitously to pair local names -with their namespaces. :api:`pyxb.namespace.NamedObjectMap` maps names to the objects +:py:obj:`pyxb.namespace.ExpandedName` is used ubiquitously to pair local names +with their namespaces. :py:obj:`pyxb.namespace.NamedObjectMap` maps names to the objects (generally, schema components) for a particular category of object. The -:api:`pyxb.namespace.NamespaceContext` class provides information related to +:py:obj:`pyxb.namespace.NamespaceContext` class provides information related to the use of namespaces in XML documents, including mappings from prefixes to namespaces. @@ -38,7 +38,7 @@ namespaces. Namespace Category Maps ----------------------- -The :api:`pyxb.namespace._NamespaceCategory_mixin` provides support for +The :py:obj:`pyxb.namespace._NamespaceCategory_mixin` provides support for discrete categories of named objects. It allows arbitrary, runtime-identified, groups of objects to be registered in individual dictionaries within the namespace. For example, XML Schema require that type @@ -56,7 +56,7 @@ and portTypes as named objects that can be identified. Namespace Component Associations -------------------------------- -The :api:`pyxb.namespace._NamespaceComponentAssociation_mixin` provides +The :py:obj:`pyxb.namespace._NamespaceComponentAssociation_mixin` provides support for associating schema components with a namespace. Of particular interest is that a namespace can be comprised of components defined from multiple sources (generally, distinct schema documents). In addition, there @@ -81,7 +81,7 @@ within XML elements, usually as values of specific attributes. The ``type`` portion of the attribute declaration above also identifies an object by name, and it must be possible to resolve the named object. The work involved in associating names with schema components is encapsulated in the -:api:`pyxb.namespace.resolution._NamespaceResolution_mixin` class. +:py:obj:`pyxb.namespace.resolution._NamespaceResolution_mixin` class. The following `concepts `_ are important to understand: @@ -100,7 +100,7 @@ important to understand: - The combination of a namespace URI and the local name comprise an `expanded namespace name `_, which is - represented by :api:`pyxb.namespace.ExpandedName`. + represented by :py:obj:`pyxb.namespace.ExpandedName`. - The category within which the local name must be resolved in the namespace is determined through external information, in the above case the fact of @@ -111,12 +111,12 @@ important to understand: pair: resolution; name pair: resolution; object (component) -:api:`pyxb.namespace._NamespaceCategory_mixin` is used to define the set of +:py:obj:`pyxb.namespace._NamespaceCategory_mixin` is used to define the set of categories supported by a namespace and to add named objects to those categories. A name is **resolved** when the object with which it is associated has been identified. Objects are **resolved** when any names on which they depend have been resolved. -:api:`pyxb.namespace.resolution._NamespaceResolution_mixin` provides a mechanism to +:py:obj:`pyxb.namespace.resolution._NamespaceResolution_mixin` provides a mechanism to hold on to names that have been encountered but whose associated objects have not yet been resolved (perhaps because the named object on which they depend has not been defined). @@ -124,16 +124,16 @@ depend has not been defined). Because one named object (e.g., a model group definition) might require resolution of another (e.g., an element reference), resolution is an iterative process, implemented by -:api:`pyxb.namespace.resolution._NamespaceResolution_mixin.resolveDefinitions`, +:py:obj:`pyxb.namespace.resolution._NamespaceResolution_mixin.resolveDefinitions`, and executed when all named objects have been added to the namespace. It -depends on :api:`pyxb.namespace.resolution.NamespaceContext` to identify named +depends on :py:obj:`pyxb.namespace.resolution.NamespaceContext` to identify named objects using the -:api:`pyxb.namespace.resolution.NamespaceContext.interpretQName` method. +:py:obj:`pyxb.namespace.resolution.NamespaceContext.interpretQName` method. Expanded Names -------------- -An :api:`pyxb.namespace.ExpandedName` instance couples a local name with +An :py:obj:`pyxb.namespace.ExpandedName` instance couples a local name with (optionally) a namespace, resulting in a QName. This class also integrates with namespace categories, permitting lookup of the object with its name in a specific category by using the category name as a method. For example, the @@ -153,7 +153,7 @@ Methods are also present to test whether the name matches a DOM node, and to retrieve the named attribute (if present) from a DOM node. In this version of PyXB, the hash codes and comparison methods for -:api:`ExpandedName ` have been overridden so that +:py:obj:`ExpandedName ` have been overridden so that an expanded name with no namespace is treated equivalently to the string value of the local name. This simplified management of default namespace lookups in earlier versions of PyXB, but may no longer be necessary; reliance on this @@ -164,7 +164,7 @@ Namespace Context `Namespaces in XML `_ specifies how the ``xmlns`` attributes are used to associate prefix strings with namespaces. -The :api:`pyxb.namespace.NamespaceContext` class supports this by associating +The :py:obj:`pyxb.namespace.NamespaceContext` class supports this by associating with each node in a DOM document the contextual information extracted from ``xmlns`` and other namespace-relevant attributes. @@ -204,7 +204,7 @@ namespace". If the target namespace for a schema is absent, we still need to be able to store things somewhere, so we represent the target namespace as a normal -:api:`pyxb.namespace.Namespace` instance, except that the associated URI is +:py:obj:`pyxb.namespace.Namespace` instance, except that the associated URI is ``None``. If in the same schema there is no default namespace, the default namespace is assigned to be this absent (but valid) target namespace, so that QName resolution works. Absence of a target namespace is the only situation @@ -223,7 +223,7 @@ Storage of Namespaces In PyXB, when the :ref:`componentModel` is used to define various elements, attributes, and types by representing them in Python instances, those instance -objects are stored in a :api:`pyxb.namespace.Namespace` instance. In addition +objects are stored in a :py:obj:`pyxb.namespace.Namespace` instance. In addition to generating code corresponding to those objects, it is possible to save the pre-computed objects into a file so that they can be referenced in other namespaces. @@ -302,7 +302,7 @@ another. The component model for a namespace is read from a namespace archive only when it is necessary to generate new bindings for a namespace that refers to it, through import or namespace declarations. The component model is defined by -invoking the :api:`pyxb.namespace.Namespace.validateComponentModel` method. +invoking the :py:obj:`pyxb.namespace.Namespace.validateComponentModel` method. Within an archive, each namespace can be marked as `private` or `public`. When the component model for a namespace is validated, all archives in which @@ -316,11 +316,11 @@ in which the namespace is marked `public`. The contents of the namespace archive are: -- A set of :api:`pyxb.namespace.archive._ModuleRecord` instances which +- A set of :py:obj:`pyxb.namespace.archive._ModuleRecord` instances which identify namespaces and mark whether they are public or private in the archive. Each instance in turn contains (for namespace ``A``): - - the set of :api:`pyxb.namespace.archive._ObjectOrigin` instances which + - the set of :py:obj:`pyxb.namespace.archive._ObjectOrigin` instances which identify the origins for components that are defined in the archive. In turn, each of these origins identifies by category and name the objects that were defined by this origin and consequently are stored in the @@ -328,7 +328,7 @@ The contents of the namespace archive are: `_ directive, multiple origins may be associated with a single module record - - the set of :api:`pyxb.namespace.Namespace` instances that were referenced + - the set of :py:obj:`pyxb.namespace.Namespace` instances that were referenced by ``A``. This includes namespaces that were imported into one of the origin objects, as well as those that were incorporated simply through reference to an object in a declared namespace diff --git a/doc/conf.py b/doc/conf.py index 6e097924..36b75ea8 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -22,7 +22,12 @@ # 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.todo', 'extapi' ] +extensions = [ + 'sphinx.ext.todo', + 'sphinx.ext.autodoc', + 'sphinx_epytext', + 'extapi', +] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -38,7 +43,7 @@ # General information about the project. project = 'PyXB' -copyright = '2009-2016, Peter A. Bigot' +copyright = '2009-2017, Peter A. Bigot' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -47,7 +52,7 @@ # The short X.Y version. version = '1.2' # The full version, including alpha/beta/rc tags. -release = '1.2.5' +release = '1.2.6' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -86,6 +91,9 @@ # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] +# Show everything useful in the API +autodoc_default_flags = ['members', 'private-members', 'undoc-members', 'show-inheritance']; + # -- Options for HTML output --------------------------------------------------- diff --git a/doc/conf.py.in b/doc/conf.py.in index 344b6920..a4fc9a31 100644 --- a/doc/conf.py.in +++ b/doc/conf.py.in @@ -22,7 +22,12 @@ import sys, os # 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.todo', 'extapi' ] +extensions = [ + 'sphinx.ext.todo', + 'sphinx.ext.autodoc', + 'sphinx_epytext', + 'extapi', +] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -86,6 +91,9 @@ pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] +# Show everything useful in the API +autodoc_default_flags = ['members', 'private-members', 'undoc-members', 'show-inheritance']; + # -- Options for HTML output --------------------------------------------------- diff --git a/doc/extapi.py b/doc/extapi.py index 09d46763..f26db9c0 100644 --- a/doc/extapi.py +++ b/doc/extapi.py @@ -1,22 +1,17 @@ -# Kenozooid - software stack to support different capabilities of dive -# computers. +# -*- coding: utf-8 -*- +# Copyright 2009-2017, Peter A. Bigot # -# Copyright (C) 2009 by Artur Wroblewski <wrobell@pld-linux.org> +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain a +# copy of the License at: # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# http://www.apache.org/licenses/LICENSE-2.0 # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# -# Modifications by PAB to override reference text +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. from __future__ import print_function import os.path @@ -26,74 +21,6 @@ __Reference_re = re.compile('\s*(.*)\s+<(.*)>\s*$', re.MULTILINE + re.DOTALL) -def api_role(role, rawtext, text, lineno, inliner, options={}, content=[]): - """ - Role `:api:` bridges generated API documentation by tool like EpyDoc - with Sphinx Python Documentation Generator. - - Other tools, other than EpyDoc, can be easily supported as well. - - First generate the documentation to be referenced, i.e. with EpyDoc:: - - $ mkdir -p doc/html/api - $ epydoc -o doc/html/api ... - - Next step is to generate documentation with Sphinx:: - - $ sphinx-build doc doc/html - - """ - basedir = 'api' - prefix = 'html/' # fixme: fetch it from configuration - exists = lambda f: os.path.exists(prefix + f) - - # assume module is references - - #print 'Text "%s"' % (text,) - mo = __Reference_re.match(text) - label = None - if mo is not None: - ( label, text ) = mo.group(1, 2) - name = text.strip() - - uri = file = '%s/%s-module.html' % (basedir, text) - chunks = text.split('.') - - #print 'Trying module file %s' % (file,) - - # if not module, then a class - if not exists(file): - name = text.split('.')[-1] - uri = file = '%s/%s-class.html' % (basedir, text) - #print 'Trying class file %s' % (file,) - - # if not a class, then function or class method - if not exists(file): - method = chunks[-1] - fprefix = '.'.join(chunks[:-1]) - # assume function is referenced - file = '%s/%s-module.html' % (basedir, fprefix) - #print 'Trying method file %s' % (file,) - if exists(file): - uri = '%s#%s' % (file, method) - else: - # class method is references - file = '%s/%s-class.html' % (basedir, fprefix) - if exists(file): - name = '.'.join(chunks[-2:]) # name should be Class.method - uri = '%s/%s-class.html#%s' % (basedir, fprefix, method) - - if label is None: - label = name - if exists(file): - node = nodes.reference(rawtext, label, refuri=uri, **options) - else: - # cannot find reference, then just inline the text - print('WARNING: Unable to find %s in API' % (text,)) - node = nodes.literal(rawtext, text) - - return [node], [] - def ticket_role(role, rawtext, text, lineno, inliner, options={}, content=[]): """ Role `:ticket:` generates references to SourceForge tickets. @@ -137,31 +64,6 @@ def issue_role(role, rawtext, text, lineno, inliner, options={}, content=[]): return [node], [] - -def pyex_role(role, rawtext, text, lineno, inliner, options={}, content=[]): - """ - Role `:pyex:` generates reference to Python exception classes. - """ - - pyex_fmt = 'http://docs.python.org/library/exceptions.html#exceptions.%s' - mo = __Reference_re.match(text) - label = None - if mo is not None: - ( label, text ) = mo.group(1, 2) - exc = text.strip() - print('Python exception %s as %s' % (text, label)) - - uri = pyex_fmt % (exc,) - if label is None: - label = '%s' % (exc,) - node = nodes.reference(rawtext, label, refuri=uri, **options) - - return [node], [] - def setup(app): - app.add_role('api', api_role) - app.add_config_value('epydoc_basedir', 'api', False) - app.add_config_value('epydoc_prefix', 'doc/html/', False) app.add_role('ticket', ticket_role) app.add_role('issue', issue_role) - app.add_role('pyex', pyex_role) diff --git a/doc/hierarchy.rst b/doc/hierarchy.rst deleted file mode 100644 index 06e21b0f..00000000 --- a/doc/hierarchy.rst +++ /dev/null @@ -1,49 +0,0 @@ -##### -Parts -##### - -******** -Chapters -******** - -Sections -======== - -Subsections ------------ - -SubSubsections -^^^^^^^^^^^^^^ - -Paragraphs -"""""""""" - -================================================== - -Overview - What it is - How to use it - Capabilities and Limitations - References - Original vision -Examples - Dictionary (Aonaware) - Simple Weather (CDyne) - Professional Weather (National Digital Forecast Data) - Television Schedules (Tribune Media Services) -Architecture - Conceptual Models - Component Model - Binding Model - Content Model - Binding Generation -User Reference - Creating bindings with genbind - Self-contained schema - Multi-document schema - Saving pre-processed namespaces - Standard bindings - WSDL -Maintainer Reference - API - Coding practices diff --git a/doc/index.txt b/doc/index.txt index 4b0e3978..7b6cfee8 100644 --- a/doc/index.txt +++ b/doc/index.txt @@ -59,6 +59,7 @@ Contents releases architecture userref_index + pyxb maintref .. _thirty_sec_example: @@ -68,7 +69,7 @@ Thirty Second Example ********************* An example of a program using PyXB to interact with a `web service -`_ using an +`_ [*]_ using an automatically-generated module. First, retrieve the WSDL and generate the bindings:: @@ -100,9 +101,14 @@ And run it:: Monday, August 18 2014: Partly Cloudy, from 67 to 83 Tuesday, August 19 2014: Partly Cloudy, from 65 to 84 -That's it. (Note: Although the `CDYNE Weather Service -`_ is -still available, the data underlying it is no longer updated.) +That's it. + +.. [*] **Note**: Sometime between 2014 and 2017 the CDYNE Weather Service + disappeared, although as of 2017-05-13 the link to its description + above was still present. If you care about weather, there is a more + complex example interfacing with the `National Digital Forecast + Database `_ in the ``examples/ndfd`` + directory. ****************** Indices and tables diff --git a/doc/limitations.txt b/doc/limitations.txt index 488f2cbe..ab7648a1 100644 --- a/doc/limitations.txt +++ b/doc/limitations.txt @@ -42,7 +42,7 @@ Things That Don't Work * `Wildcard elements `_ are supported in the sense that classes that enable them in the content model provide access to unrecognized values through the - :api:`pyxb.binding.basis.complexTypeDefinition.wildcardElements` method. + :py:obj:`pyxb.binding.basis.complexTypeDefinition.wildcardElements` method. Where the type of the node is recognized, it is converted to a Python binding instance; otherwise, the xml DOM instance is stored. Although namespace constraints are applied when checking for wildcard matches, the @@ -52,7 +52,7 @@ Things That Don't Work `_ are supported in much the same sense as wildcard elements: no constraints are checked, and all are aggregated into the - :api:`pyxb.binding.basis.complexTypeDefinition.wildcardAttributeMap`. + :py:obj:`pyxb.binding.basis.complexTypeDefinition.wildcardAttributeMap`. Values are uninterpreted Unicode strings. ...and probably never will diff --git a/doc/maintref.txt b/doc/maintref.txt index 1addc74f..326d5142 100644 --- a/doc/maintref.txt +++ b/doc/maintref.txt @@ -2,32 +2,22 @@ Maintainer Reference ******************** -The `API `_ -*************************** - -The `API `_ for PyXB was generated using `Epydoc -`_. Most of the public API is commented, -though not all docstrings have been updated to use Epydoc syntax. - .. _git_repository: Git Repository ************** -PyXB is developed using `git `_. You can check out the -development branch with:: - - git clone git://pyxb.git.sourceforge.net/gitroot/pyxb/pyxb - +PyXB is developed using `git `_ with active +development hosted on `Github `_. Public development is mostly done on the ``next`` branch, which is the -default for checkouts from SourceForge. The ``master`` branch contains -material integrated into the release, and follows ``next``. Tags for each +default for cloned checkouts. The ``master`` branch contains material +integrated into the release, and follows ``next``. Tags for each release are in the format ``PyXB-X.Y.Z``. -Bug fixes with unit tests are pushed to the ``next`` branch as soon as they -are fixed. Users whose reported issues have been fixed are encouraged to -use the development branch until the fix has been made available in a tagged -and packaged release. +Bug fixes with unit tests are pushed to the ``next`` branch as soon as +they are fixed. Users whose reported issues have been fixed are +encouraged to use the development branch until the fix has been made +available in a tagged and packaged release. Coding Practices **************** @@ -96,14 +86,14 @@ PyXB provides a `standard exception hierarchy Where an execution branch has been identified that requires behavior that has not yet been implemented, raise an -:api:`pyxb.exceptions_.IncompleteImplementationError`. At the current stage +:py:obj:`pyxb.exceptions_.IncompleteImplementationError`. At the current stage of development, these should be extremely rare. Where the system detects that a precondition is not satisfied, processing must stop. If the precondition failure is due to improper use of the PyXB -internal or public API, a :api:`pyxb.exceptions_.LogicError` should be +internal or public API, a :py:obj:`pyxb.exceptions_.LogicError` should be raised. If the precondition failure is due to invalid input from the -schema, a :api:`pyxb.exceptions_.SchemaValidationError` should be raised. +schema, a :py:obj:`pyxb.exceptions_.SchemaValidationError` should be raised. If the precondition is inexplicably false, Python's ``assert`` functionality may be used. Use of ``assert`` should be rare, and only in places that are @@ -121,11 +111,13 @@ us to Python 2.4 or later. Sigh with disappointment and move on. Documentation ============= -Use the `Epytext Markup Language -`_ for all public and -implementation-shared methods and classes. (Formerly, this was "Use -docstrings :PEP:`257`". Documentation in only a few modules has been -converted.) +Use Sphinx-compatible documentation for all public and +implementation-shared methods and classes. (Formerly, this was "Use the +`Epytext Markup Language +`_" but epydoc hasn't +been updated since before PyXB was born. Few if any modules have been +converted. ) (Formerly, this was "Use docstrings :PEP:`257`". +Documentation in only a few modules has been converted.) Comments ======== @@ -216,8 +208,8 @@ Class Variables At Multiple Levels There are several cases where we want to store information in a class, but allow subclasses (not instances) to override it. An example is in the -:api:`pyxb.binding.basis.simpleTypeDefinition` hierarchy where each class -maintains a set of :api:`pyxb.binding.facets.ConstrainingFacet` instances +:py:obj:`pyxb.binding.basis.simpleTypeDefinition` hierarchy where each class +maintains a set of :py:obj:`pyxb.binding.facets.ConstrainingFacet` instances that are available for constraining values of the class. In many cases, a subclass will not change the set of facets that affect instances, so we want to be able to inherit the parent class map; but in other cases we may need diff --git a/doc/pyxb.binding.txt b/doc/pyxb.binding.txt new file mode 100644 index 00000000..716a237e --- /dev/null +++ b/doc/pyxb.binding.txt @@ -0,0 +1,70 @@ +pyxb\.binding package +===================== + +Submodules +---------- + +pyxb\.binding\.basis module +--------------------------- + +.. automodule:: pyxb.binding.basis + :members: + :undoc-members: + :show-inheritance: + +pyxb\.binding\.content module +----------------------------- + +.. automodule:: pyxb.binding.content + :members: + :undoc-members: + :show-inheritance: + +pyxb\.binding\.datatypes module +------------------------------- + +.. automodule:: pyxb.binding.datatypes + :members: + :undoc-members: + :show-inheritance: + +pyxb\.binding\.facets module +---------------------------- + +.. automodule:: pyxb.binding.facets + :members: + :undoc-members: + :show-inheritance: + +pyxb\.binding\.generate module +------------------------------ + +.. automodule:: pyxb.binding.generate + :members: + :undoc-members: + :show-inheritance: + +pyxb\.binding\.saxer module +--------------------------- + +.. automodule:: pyxb.binding.saxer + :members: + :undoc-members: + :show-inheritance: + +pyxb\.binding\.xml\_ module +--------------------------- + +.. automodule:: pyxb.binding.xml_ + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: pyxb.binding + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/pyxb.namespace.txt b/doc/pyxb.namespace.txt new file mode 100644 index 00000000..f72ad843 --- /dev/null +++ b/doc/pyxb.namespace.txt @@ -0,0 +1,46 @@ +pyxb\.namespace package +======================= + +Submodules +---------- + +pyxb\.namespace\.archive module +------------------------------- + +.. automodule:: pyxb.namespace.archive + :members: + :undoc-members: + :show-inheritance: + +pyxb\.namespace\.builtin module +------------------------------- + +.. automodule:: pyxb.namespace.builtin + :members: + :undoc-members: + :show-inheritance: + +pyxb\.namespace\.resolution module +---------------------------------- + +.. automodule:: pyxb.namespace.resolution + :members: + :undoc-members: + :show-inheritance: + +pyxb\.namespace\.utility module +------------------------------- + +.. automodule:: pyxb.namespace.utility + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: pyxb.namespace + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/pyxb.txt b/doc/pyxb.txt new file mode 100644 index 00000000..6808e7e9 --- /dev/null +++ b/doc/pyxb.txt @@ -0,0 +1,34 @@ +pyxb package +============ + +.. automodule:: pyxb + +subpackages +----------- + +.. toctree:: + + pyxb.binding + pyxb.namespace + pyxb.utils + pyxb.xmlschema + +Submodules +---------- + +pyxb\.exceptions\_ module +------------------------- + +.. automodule:: pyxb.exceptions_ + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: pyxb + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/pyxb.utils.txt b/doc/pyxb.utils.txt new file mode 100644 index 00000000..2681d070 --- /dev/null +++ b/doc/pyxb.utils.txt @@ -0,0 +1,102 @@ +pyxb\.utils package +=================== + +Submodules +---------- + +pyxb\.utils\.activestate module +------------------------------- + +.. automodule:: pyxb.utils.activestate + :members: + :undoc-members: + :show-inheritance: + +pyxb\.utils\.domutils module +---------------------------- + +.. automodule:: pyxb.utils.domutils + :members: + :undoc-members: + :show-inheritance: + +pyxb\.utils\.fac module +----------------------- + +.. automodule:: pyxb.utils.fac + :members: + :undoc-members: + :show-inheritance: + +pyxb\.utils\.saxdom module +-------------------------- + +.. automodule:: pyxb.utils.saxdom + :members: + :undoc-members: + :show-inheritance: + +pyxb\.utils\.saxutils module +---------------------------- + +.. automodule:: pyxb.utils.saxutils + :members: + :undoc-members: + :show-inheritance: + +pyxb\.utils\.six module +----------------------- + +.. automodule:: pyxb.utils.six + :members: + :undoc-members: + :show-inheritance: + +pyxb\.utils\.templates module +----------------------------- + +.. automodule:: pyxb.utils.templates + :members: + :undoc-members: + :show-inheritance: + +pyxb\.utils\.unicode module +--------------------------- + +.. automodule:: pyxb.utils.unicode + :members: + :undoc-members: + :show-inheritance: + +pyxb\.utils\.unicode\_data module +--------------------------------- + +.. automodule:: pyxb.utils.unicode_data + :members: + :undoc-members: + :show-inheritance: + +pyxb\.utils\.utility module +--------------------------- + +.. automodule:: pyxb.utils.utility + :members: + :undoc-members: + :show-inheritance: + +pyxb\.utils\.xmlre module +------------------------- + +.. automodule:: pyxb.utils.xmlre + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: pyxb.utils + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/pyxb.xmlschema.txt b/doc/pyxb.xmlschema.txt new file mode 100644 index 00000000..29704681 --- /dev/null +++ b/doc/pyxb.xmlschema.txt @@ -0,0 +1,22 @@ +pyxb\.xmlschema package +======================= + +Submodules +---------- + +pyxb\.xmlschema\.structures module +---------------------------------- + +.. automodule:: pyxb.xmlschema.structures + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: pyxb.xmlschema + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/releases.txt b/doc/releases.txt index c6c534c7..3536f75d 100644 --- a/doc/releases.txt +++ b/doc/releases.txt @@ -41,11 +41,42 @@ dropped for Python 2.4 and 2.5. The 1.2.x release works with Python 2.6 and skipped under Python 2.6 due to insufficient support for validating exception attributes. +.. _pyxb-1.2.6: + +1.2.6 (04 Sep 2017) +------------------- + +This is a roll-up aggregating all fixes since the last major release one year +ago. Release testing has been performed with Python 2.7.12, 3.4.7, 3.5.4, and +3.6.2. + +Behavioral changes: + +- Overriding the DOM implementation via `PYXB_XML_STYLE` is deprecated. See + :issue:`87`. + +The following reported `defects/enhancements +`_ have been addressed: + +- Examples were not in the test suite. :issue:`61` +- Disallow setting an empty namespace prefix. :issue:`66` +- Support unicode identifiers under Python 3. :issue:`67` +- Do not use default value when constructing empty scalar values. :issue:`71` +- Defer validation when type is not yet available. :issue:`72` +- Correct validation of fixed values from XML. :issue:`73` +- Remove GPL code from PyXB. :issue:`77` +- Fix identification of restricted complex type. :issue:`78` +- Discard schema elements that cannot produce content. :issue:`79` +- Demonstrate use of overriding built-in bindings so XHTML model groups can be + referenced. :issue:`81` + .. _pyxb-1.2.5: 1.2.5 (18 Sep 2016) ------------------- +Documentation: http://pyxb.sourceforge.net/PyXB-1.2.5 + This is a roll-up aggregating all fixes since the last major release nearly two years ago. @@ -188,7 +219,7 @@ The following reported `defects/enhancements :ref:`pyxbgen--wsdl-location`. :ticket:`210` - Refine diagnostics when an element with simple type appears in a context that expects non-element content. :ticket:`211` -- Add :api:`pyxb.NonElementContent` to simplify access to non-element content +- Add :py:obj:`pyxb.NonElementContent` to simplify access to non-element content in a mixed-content instance. :ticket:`212` - Convert assert failures to diagnostic exceptions when generating DOM expressions of complex types with simple content where the content is @@ -239,9 +270,9 @@ Documentation: http://pyxb.sourceforge.net/PyXB-1.2.1 .. warning:: - This release has an interface change: the :api:`content + This release has an interface change: the :py:obj:`content ` method on complex types - is now deprecated in favor of the :api:`orderedContent + is now deprecated in favor of the :py:obj:`orderedContent ` method. The members in the new list have wrapper classes to correctly associate instance bindings with the element declaration and to distinguish instances @@ -252,7 +283,7 @@ Documentation: http://pyxb.sourceforge.net/PyXB-1.2.1 Key features of the release: - Full support for mixed content schema through a new method - :api:`orderedContent ` on complex + :py:obj:`orderedContent ` on complex binding instances and flags that control when that list affects document generation. See :ref:`mixed_content`. This is particularly relevant to XHTML. @@ -260,7 +291,7 @@ Key features of the release: - Immediate validation of values assigned to plural elements. - A first step to providing finer control of validation, using - :api:`pyxb.ValidationConfig` + :py:obj:`pyxb.ValidationConfig` The following reported `defects/enhancements `_ have been addressed: @@ -513,7 +544,7 @@ not acceptable to the validating parser, and where it can be found in the document. See :ref:`invalid-content`. Also, using keyword parameters in binding instance constructors will now raise -an :api:`pyxb.exceptions_.ExtraContentError` in the case where the keyword does not +an :py:obj:`pyxb.exceptions_.ExtraContentError` in the case where the keyword does not correspond to the PyXB-generated identifier associated with an attribute or element of the class being instantiated. diff --git a/doc/userref_pyxbgen.txt b/doc/userref_pyxbgen.txt index d688b0cf..b05a6c34 100644 --- a/doc/userref_pyxbgen.txt +++ b/doc/userref_pyxbgen.txt @@ -150,7 +150,7 @@ namespace, so they can be referenced in independent generation activities. To generate the archive, you add the :ref:`pyxbgen--archive-to-file` flag to the binding generation command: -.. literalinclude:: ../examples/manual/demo3c.sh +.. literalinclude:: ../examples/manual/demo3c1.sh In addition to generating the ``address`` Python module, this causes a :ref:`archive ` of the schema contents to be saved in the @@ -161,7 +161,7 @@ in this archive, so that cross-namespace extension works correctly. You can then generate bindings for importing namespaces by providing PyXB with the information necessary to locate this archive: -.. literalinclude:: ../examples/manual/demo3d.sh +.. literalinclude:: ../examples/manual/demo3c2.sh The :ref:`pyxbgen--archive-path` directive indicates that the current directory (``.``) should be searched for files that end in ``.wxs``, and any @@ -244,11 +244,11 @@ create instances of the customized class when automatically generating ``tParam`` instances from XML documents. To customize bindings, you will need to be familiar with the -:api:`pyxb.binding.basis._DynamicCreate_mixin` class. +:py:obj:`pyxb.binding.basis._DynamicCreate_mixin` class. -Be aware that :api:`_SetSupersedingClass +Be aware that :py:obj:`_SetSupersedingClass ` only affects the behavior of -:api:`Factory `, and does not change +:py:obj:`Factory `, and does not change the Python inheritance tree. This means that the superseding class is only invoked when the content model requires an instance of the original type. When an instance of a subclass of a superseded class (that is not itself diff --git a/doc/userref_usebind.txt b/doc/userref_usebind.txt index 5c196d16..f0a700db 100644 --- a/doc/userref_usebind.txt +++ b/doc/userref_usebind.txt @@ -27,7 +27,7 @@ Locating Invalid Content ^^^^^^^^^^^^^^^^^^^^^^^^ If a document does not validate, PyXB will generally through an -:api:`pyxb.UnrecognizedContentError` exception. You can determine where the +:py:obj:`pyxb.UnrecognizedContentError` exception. You can determine where the problem lies, and what was not recognized, by examining attributes present on the exception as shown in this example: @@ -44,7 +44,7 @@ Some web services and binding tools mis-use `xsi:type `_, providing attribute values that either are not types, or do not specify a type that is derived from an abstract type. The -:api:`pyxb.namespace.builtin.XMLSchema_instance.ProcessTypeAttribute +:py:obj:`pyxb.namespace.builtin.XMLSchema_instance.ProcessTypeAttribute ` method can be used to relax how PyXB processes those attributes. @@ -98,7 +98,7 @@ schema does not include New York as a state, so the following assignment:: addr.state = 'NY' -will cause a :api:`pyxb.exceptions_.BadTypeValueError` exception to be raised: +will cause a :py:obj:`pyxb.exceptions_.BadTypeValueError` exception to be raised: .. literalinclude:: ../examples/manual/demo4a1.out @@ -130,10 +130,10 @@ This example produces (after reformatting): Note that, because we're in the middle of the example and have not provided the ``items`` element that the content model requires, the code -:api:`explicitly disables the requirement for +:py:obj:`explicitly disables the requirement for validation ` when generating XML from a binding instance. A consequence of this is that the generated XML is not -valid, and validation must be :api:`disabled for parsing +valid, and validation must be :py:obj:`disabled for parsing ` as well if the resulting document is to be re-converted into a binding with ``CreateFromDocument``. @@ -152,11 +152,11 @@ the deeper elements of the purchase order: In particular, there is no global ``item`` element that can be used to create the individual items. For situations like this, we use -:api:`pyxb.BIND`: +:py:obj:`pyxb.BIND`: .. literalinclude:: ../examples/manual/demo4c1.py -The :api:`pyxb.BIND` reference wraps the content of the inner elements, and +The :py:obj:`pyxb.BIND` reference wraps the content of the inner elements, and is a cue to PyXB to attempt to build an instance of whatever type of object would satisfy the content model at that point. The resulting document (after reformatting) is: @@ -186,7 +186,7 @@ The ``toxml`` method is short-hand for a sequence that converts the binding to a DOM instance using ``xml.dom.minidom``, then uses the DOM interface to generate the XML document. -The :api:`pyxb.utils.domutils.BindingDOMSupport` class provides ways to +The :py:obj:`pyxb.utils.domutils.BindingDOMSupport` class provides ways to control this generation. In particular, you may want to use something more informative than ``ns#`` to denote namespaces in the generated documents. This can be done using the following code: @@ -229,32 +229,32 @@ elements appear in the original schema. As of release 1.2.1 [#content]_, PyXB appends both element and non-element content to a list in each complex binding instance. The list may be obtained using the -:api:`orderedContent +:py:obj:`orderedContent ` method. The list -comprises instances of :api:`pyxb.binding.basis.ElementContent` and -:api:`pyxb.binding.basis.NonElementContent` added in the order in which they were +comprises instances of :py:obj:`pyxb.binding.basis.ElementContent` and +:py:obj:`pyxb.binding.basis.NonElementContent` added in the order in which they were added to the binding instance: when creating the instance from a document or through a constructor, or by invoking the -:api:`append ` or -:api:`extend ` methods to add +:py:obj:`append ` or +:py:obj:`extend ` methods to add content consistent with the content model. -The :api:`contentInfluencesGeneration +The :py:obj:`contentInfluencesGeneration ` flag of -:api:`pyxb.ValidationConfig` controls how the ``orderedContent`` list affects +:py:obj:`pyxb.ValidationConfig` controls how the ``orderedContent`` list affects generation of documents (both DOM directly and XML indirectly). With the -default value of :api:`MIXED_ONLY ` the +default value of :py:obj:`MIXED_ONLY ` the ``orderedContent`` list is only consulted when a complex type allows both element and non-element content. The bundle for XHTML has been modified to use: -- :api:`ALWAYS ` for :api:`contentInfluencesGeneration +- :py:obj:`ALWAYS ` for :py:obj:`contentInfluencesGeneration ` -- :api:`RAISE_EXCEPTION ` for - :api:`orphanElementInContent ` -- :api:`RAISE_EXCEPTION ` for - :api:`invalidElementInContent ` +- :py:obj:`RAISE_EXCEPTION ` for + :py:obj:`orphanElementInContent ` +- :py:obj:`RAISE_EXCEPTION ` for + :py:obj:`invalidElementInContent ` for all binding classes in that module. (See ``pyxb/bundles/common/xhtml1.py`` for the technique used.) This ensures @@ -279,7 +279,7 @@ incorrect in at least two cases: the newly added elements do not appear in the ``orderedContent`` list. -The value returned by :api:`orderedContent +The value returned by :py:obj:`orderedContent ` is a mutable list so that you can manipulate it to reflect the content you wish to have generated. @@ -287,9 +287,9 @@ generated. Where the ``orderedContent`` list is not consistent with the content model (e.g., references elements that are no longer part of the binding instance, or proposes an order that is not valid) various exceptions may arise. To some -extent this can be controlled through the :api:`orphanElementInContent +extent this can be controlled through the :py:obj:`orphanElementInContent ` and -:api:`invalidElementInContent ` +:py:obj:`invalidElementInContent ` flags. .. [#content] Though previous versions also provided this information through diff --git a/doc/userref_validating.txt b/doc/userref_validating.txt index f5c8b5e8..b9fbb7cd 100644 --- a/doc/userref_validating.txt +++ b/doc/userref_validating.txt @@ -53,7 +53,7 @@ instance: r = DWML.CreateFromDocument(rxml) When invoking the program, the following diagnostic output is provided -through the :api:`details ` +through the :py:obj:`details ` method: .. code-block:: none @@ -98,7 +98,7 @@ This tells the user that: A much shorter but still useful synopsis of the invalidity would be available through the ``str`` operation on the exception. Full details are -available through attributes on the :api:`UnrecognizedContentError +available through attributes on the :py:obj:`UnrecognizedContentError ` and other exceptions. In cases where the service generating the documents is under your control, @@ -112,7 +112,7 @@ Runtime Exception Hierarchy --------------------------- Details on the interfaces presented by these exceptions can be found through -the :api:`API Documentation `. +the :py:obj:`API Documentation `. .. image:: Images/RuntimeExceptions.jpg diff --git a/examples/cablelabs/disabled-test.sh b/examples/cablelabs/disabled-test.sh new file mode 100755 index 00000000..5e84c356 --- /dev/null +++ b/examples/cablelabs/disabled-test.sh @@ -0,0 +1,3 @@ +rm -f cablelabs.wxs +sh genbindings.sh \ + && python demo.py diff --git a/examples/cablelabs/test.sh b/examples/cablelabs/test.sh deleted file mode 100755 index 82e4edab..00000000 --- a/examples/cablelabs/test.sh +++ /dev/null @@ -1,3 +0,0 @@ -#rm -f cablelabs.wxs -#sh genbindings.sh \ -# && python demo.py diff --git a/examples/content/test.expected b/examples/content/test.expected new file mode 100644 index 00000000..37ec9815 --- /dev/null +++ b/examples/content/test.expected @@ -0,0 +1,4 @@ +12 +3 +8 +15 diff --git a/examples/content/test.sh b/examples/content/test.sh index b92fab93..9a39e3a3 100755 --- a/examples/content/test.sh +++ b/examples/content/test.sh @@ -1,5 +1,12 @@ +#!/bin/sh + +: ${PYXB_TEST_ROOT:=${PYXB_ROOT}/tests} +. ${PYXB_TEST_ROOT}/support.sh + rm -f content.py pyxbgen \ - -u content.xsd -m content \ - && python showcontent.py > showcontent.out \ - && cat showcontent.out + -u content.xsd -m content \ + || fail generating bindings +python showcontent.py > test.out || fail running +cmp test.out test.expected || fail output comparison +passed diff --git a/examples/customization/test.sh b/examples/customization/test.sh index fc192f09..831ae4ed 100755 --- a/examples/customization/test.sh +++ b/examples/customization/test.sh @@ -1,9 +1,7 @@ #!/bin/sh -fail () { - echo 1>&2 "${test_name} FAILED: ${@}" - exit 1 -} +: ${PYXB_TEST_ROOT:=${PYXB_ROOT}/tests} +. ${PYXB_TEST_ROOT}/support.sh xmllint --schema custom.xsd test.xml || fail Test document is not valid @@ -11,6 +9,8 @@ rm -rf raw *.pyc pyxbgen \ -m custom \ -u custom.xsd \ - --write-for-customization + --write-for-customization \ + || fail generating bindings python tst-normal.py || fail Normal customization failed python tst-introspect.py || fail Introspection customization failed +passed diff --git a/examples/dictionary/showdict.expected b/examples/dictionary/showdict.expected new file mode 100644 index 00000000..bacedf15 --- /dev/null +++ b/examples/dictionary/showdict.expected @@ -0,0 +1,11 @@ +THE DEVIL'S DICTIONARY ((C)1911 Released April 15 1993) (devils) : 3235 chars +Easton's 1897 Bible Dictionary (easton) : 2182 chars +Elements database 20001107 (elements) : 1285 chars +The Free On-line Dictionary of Computing (27 SEP 03) (foldoc) : 3952 chars +U.S. Gazetteer (1990) (gazetteer) : 604 chars +The Collaborative International Dictionary of English v.0.44 (gcide) : 3168 chars +Hitchcock's Bible Names Dictionary (late 1800's) (hitchcock) : 1021 chars +Jargon File (4.3.1, 29 Jun 2001) (jargon) : 2992 chars +Virtual Entity of Relevant Acronyms (Version 1.9, June 2002) (vera) : 1355 chars +WordNet (r) 2.0 (wn) : 2562 chars +CIA World Factbook 2002 (world02) : 2544 chars diff --git a/examples/dictionary/showdict.py b/examples/dictionary/showdict.py index 6b856e1b..08b3b893 100644 --- a/examples/dictionary/showdict.py +++ b/examples/dictionary/showdict.py @@ -1,4 +1,7 @@ +# -*- coding: utf-8 -*- + from __future__ import print_function +import sys import dict from pyxb.utils.six.moves.urllib.request import urlopen import pyxb.utils.domutils as domutils @@ -16,9 +19,12 @@ # Create a REST-style query to retrieve the information about this dictionary. uri = '%s%s?dictId=%s' % (port_uri, op_path, d.Id) resp = urlopen(uri).read() + print("%s (%s) : %d chars" % (d.Name, d.Id, len(resp))); + # The response is a simple type derived from string, so we can - # just extract and print it. + # just extract and print it. Excluded by default since it has + # leading and trailing whitespace that causes problems with using + # git to store the expected output. di_resp = dict.CreateFromDOM(domutils.StringToDOM(resp)) - # Do the "encode" garbage because one of these dictionaries has a - # non-ASCII character - print("%s (%s)\n%s\n" % (d.Name.encode('utf-8'), d.Id.encode('utf-8'), di_resp.encode('utf-8'))) + if sys.stdout.isatty(): + print("%s\n" % di_resp); diff --git a/examples/dictionary/test.sh b/examples/dictionary/test.sh index 60a19eff..b1cd9a9f 100755 --- a/examples/dictionary/test.sh +++ b/examples/dictionary/test.sh @@ -1,2 +1,9 @@ -sh genbindings.sh -python showdict.py +#!/bin/sh + +: ${PYXB_TEST_ROOT:=${PYXB_ROOT}/tests} +. ${PYXB_TEST_ROOT}/support.sh + +sh genbindings.sh || fail generating bindings +python showdict.py > showdict.out || fail running +cmp showdict.out showdict.expected || fail output comparison +passed diff --git a/examples/geocoder/xtest.sh b/examples/geocoder/disabled-test.sh similarity index 100% rename from examples/geocoder/xtest.sh rename to examples/geocoder/disabled-test.sh diff --git a/examples/manual/badcontent.expected b/examples/manual/badcontent.expected new file mode 100644 index 00000000..4ad65c6d --- /dev/null +++ b/examples/manual/badcontent.expected @@ -0,0 +1,6 @@ +The containing element shipTo is defined at po1.xsd[6:6]. +The containing element type USAddress is defined at po1.xsd[13:2] +The unrecognized content streeet begins at badcontent.xml[5:4] +The USAddress automaton is not in an accepting state. +The following element and wildcard content would be accepted: + An element street per po1.xsd[16:6] diff --git a/examples/manual/demo.expected b/examples/manual/demo.expected new file mode 100644 index 00000000..2aa8a409 --- /dev/null +++ b/examples/manual/demo.expected @@ -0,0 +1,3 @@ +Robert Smith is sending Alice Smith 2 thing(s): + Quantity 1 of Lapis necklace at $99.95 + Quantity 4 of Plastic necklace at $3.95 diff --git a/examples/manual/demo3c.sh b/examples/manual/demo3c1.sh similarity index 100% rename from examples/manual/demo3c.sh rename to examples/manual/demo3c1.sh diff --git a/examples/manual/demo3d.sh b/examples/manual/demo3c2.sh similarity index 100% rename from examples/manual/demo3d.sh rename to examples/manual/demo3c2.sh diff --git a/examples/manual/demo4.expected b/examples/manual/demo4.expected new file mode 100644 index 00000000..6eb0d259 --- /dev/null +++ b/examples/manual/demo4.expected @@ -0,0 +1 @@ +Robert Smith8 Oak AvenueAnytownAK12341 diff --git a/examples/manual/demo4a.py b/examples/manual/demo4a.py index 5d49db0f..130736d3 100644 --- a/examples/manual/demo4a.py +++ b/examples/manual/demo4a.py @@ -8,4 +8,4 @@ addr.state = 'AK' addr.zip = 12341 -print(addr.toxml("utf-8", element_name='USAddress')) +print(addr.toxml("utf-8", element_name='USAddress').decode('utf-8')) diff --git a/examples/manual/demo4a1.expected b/examples/manual/demo4a1.expected new file mode 100644 index 00000000..09f15d7e --- /dev/null +++ b/examples/manual/demo4a1.expected @@ -0,0 +1,18 @@ +Traceback (most recent call last): + File "demo4a1.py", line #, in + addr.state = 'NY' + File "PYXB_ROOT/pyxb/binding/basis.py", line #, in __setattr__ + return super(_TypeBinding_mixin, self).__setattr__(name, value) + File "PYXB_ROOT/pyxb/binding/content.py", line #, in set + value = self.__elementBinding.compatibleValue(value, is_plural=self.isPlural()) + File "PYXB_ROOT/pyxb/binding/basis.py", line #, in compatibleValue + compValue = self.typeDefinition()._CompatibleValue(value, **kw); + File "PYXB_ROOT/pyxb/binding/basis.py", line #, in _CompatibleValue + return cls(value) + File "PYXB_ROOT/pyxb/binding/basis.py", line #, in __init__ + self.xsdConstraintsOK(location) + File "PYXB_ROOT/pyxb/binding/basis.py", line #, in xsdConstraintsOK + return self.XsdConstraintsOK(self, location) + File "PYXB_ROOT/pyxb/binding/basis.py", line #, in XsdConstraintsOK + raise pyxb.SimpleFacetValueError(cls, value, f, location) +pyxb.exceptions_.SimpleFacetValueError: Type {URN:address}USState enumeration constraint violated by value NY diff --git a/examples/manual/demo4a1.out b/examples/manual/demo4a1.out index a68f10ab..e69de29b 100644 --- a/examples/manual/demo4a1.out +++ b/examples/manual/demo4a1.out @@ -1,18 +0,0 @@ -Traceback (most recent call last): - File "demo4a1.py", line 8, in - addr.state = 'NY' - File "/opt/pyxb/pyxb/binding/basis.py", line 100, in __setattr__ - return super(_TypeBinding_mixin, self).__setattr__(name, value) - File "/opt/pyxb/pyxb/binding/content.py", line 1037, in set - value = self.__elementBinding.compatibleValue(value, is_plural=self.isPlural()) - File "/opt/pyxb/pyxb/binding/basis.py", line 1623, in compatibleValue - return self.typeDefinition()._CompatibleValue(value, **kw) - File "/opt/pyxb/pyxb/binding/basis.py", line 373, in _CompatibleValue - return cls(value) - File "/opt/pyxb/pyxb/binding/basis.py", line 924, in __init__ - self.xsdConstraintsOK(location) - File "/opt/pyxb/pyxb/binding/basis.py", line 1050, in xsdConstraintsOK - return self.XsdConstraintsOK(self, location) - File "/opt/pyxb/pyxb/binding/basis.py", line 1045, in XsdConstraintsOK - raise pyxb.SimpleFacetValueError(cls, value, f, location) -pyxb.exceptions_.SimpleFacetValueError: Type {URN:address}USState enumeration constraint violated by value NY diff --git a/examples/manual/demo4a1.py b/examples/manual/demo4a1.py index db8a2b1c..fc4220c7 100644 --- a/examples/manual/demo4a1.py +++ b/examples/manual/demo4a1.py @@ -8,4 +8,4 @@ addr.state = 'NY' addr.zip = 12341 -print(addr.toxml("utf-8")) +print(addr.toxml("utf-8").decode('utf-8')) diff --git a/examples/manual/demo4a2.py b/examples/manual/demo4a2.py index 00ec8f1e..41c12cdb 100644 --- a/examples/manual/demo4a2.py +++ b/examples/manual/demo4a2.py @@ -8,4 +8,4 @@ addr.zip = 12341 addr.name = 'Robert Smith' -print(addr.toxml("utf-8", element_name='USAddress')) +print(addr.toxml("utf-8", element_name='USAddress').decode('utf-8')) diff --git a/examples/manual/demo4b.py b/examples/manual/demo4b.py index 12db6e71..e6ed17a6 100644 --- a/examples/manual/demo4b.py +++ b/examples/manual/demo4b.py @@ -5,4 +5,4 @@ addr = address.USAddress('Robert Smith', '8 Oak Avenue', 'Anytown', 'AK', 12341) -print(addr.toxml("utf-8", element_name='USAddress')) +print(addr.toxml("utf-8", element_name='USAddress').decode('utf-8')) diff --git a/examples/manual/demo4c.py b/examples/manual/demo4c.py index 90c220f6..a4f44d1d 100644 --- a/examples/manual/demo4c.py +++ b/examples/manual/demo4c.py @@ -10,5 +10,6 @@ po.shipTo = address.USAddress('Alice Smith', '123 Maple Street', 'Anytown', 'AK', 12341) po.billTo = address.USAddress('Robert Smith', '8 Oak Avenue', 'Anytown', 'AK', 12341) +# Disable validation since content is incomplete. pyxb.RequireValidWhenGenerating(False) -print(po.toxml("utf-8")) +print(po.toxml("utf-8").decode('utf-8')) diff --git a/examples/manual/demo4c1.expected b/examples/manual/demo4c1.expected new file mode 100644 index 00000000..0938dad7 --- /dev/null +++ b/examples/manual/demo4c1.expected @@ -0,0 +1,29 @@ + + + + Alice Smith + 123 Maple Street + Anytown + AK + 12341 + + + Robert Smith + 8 Oak Avenue + Anytown + AK + 12341 + + + + Lapis necklace + 1 + 99.95 + + + Plastic necklace + 4 + 3.95 + + + diff --git a/examples/manual/demo4c1.py b/examples/manual/demo4c1.py index 280bed76..38727f23 100644 --- a/examples/manual/demo4c1.py +++ b/examples/manual/demo4c1.py @@ -10,4 +10,4 @@ po.items = pyxb.BIND(pyxb.BIND('Lapis necklace', 1, 99.95, partNum='833-AA'), pyxb.BIND('Plastic necklace', 4, 3.95, partNum='833-AB')) -print(po.toxml("utf-8")) +print(po.toxml("utf-8").decode('utf-8')) diff --git a/examples/manual/demo4c2.expected b/examples/manual/demo4c2.expected new file mode 100644 index 00000000..2f918053 --- /dev/null +++ b/examples/manual/demo4c2.expected @@ -0,0 +1,32 @@ + + + + Alice Smith + 123 Maple Street + Anytown + AK + 12341 + + + Robert Smith + 8 Oak Avenue + Anytown + AK + 12341 + + + + Lapis necklace + 1 + 99.95 + Want this for the holidays! + 1999-12-05 + + + Plastic necklace + 4 + 3.95 + 1999-12-24 + + + diff --git a/examples/manual/demo4c2.py b/examples/manual/demo4c2.py index 9f42b112..de8e21f7 100644 --- a/examples/manual/demo4c2.py +++ b/examples/manual/demo4c2.py @@ -18,4 +18,4 @@ lapis.comment = 'Want this for the holidays!' po.items.item[1].shipDate = po.items.item[0].shipDate + datetime.timedelta(days=19) -print(po.toxml("utf-8")) +print(po.toxml("utf-8").decode('utf-8')) diff --git a/examples/manual/demo4c3.expected b/examples/manual/demo4c3.expected new file mode 100644 index 00000000..23af5d71 --- /dev/null +++ b/examples/manual/demo4c3.expected @@ -0,0 +1,32 @@ + + + + Alice Smith + 123 Maple Street + Anytown + AK + 12341 + + + Robert Smith + 8 Oak Avenue + Anytown + AK + 12341 + + + + Lapis necklace + 1 + 99.95 + Want this for the holidays! + 1999-12-05 + + + Plastic necklace + 4 + 3.95 + 1999-12-24 + + + diff --git a/examples/manual/demo4c3.py b/examples/manual/demo4c3.py index 5bc8f551..7c6c11d1 100644 --- a/examples/manual/demo4c3.py +++ b/examples/manual/demo4c3.py @@ -22,4 +22,4 @@ pyxb.utils.domutils.BindingDOMSupport.DeclareNamespace(address.Namespace, 'addr') pyxb.utils.domutils.BindingDOMSupport.DeclareNamespace(po4.Namespace, 'po') -print(po.toxml("utf-8")) +print(po.toxml("utf-8").decode('utf-8')) diff --git a/examples/manual/test.sh b/examples/manual/test.sh new file mode 100755 index 00000000..f4f3ed00 --- /dev/null +++ b/examples/manual/test.sh @@ -0,0 +1,70 @@ +#!/bin/sh + +: ${PYXB_TEST_ROOT:=${PYXB_ROOT}/tests} +. ${PYXB_TEST_ROOT}/support.sh + +rm -f *.wxs po?.py *.pyc + +sh demo1.sh || fail building demo1 +python demo1.py > demo1.out || fail running demo1 +cat demo1.out +cmp demo1.out demo.expected || fail demo1 output check + +sh demo2.sh || fail building demo2 +python demo2.py > demo2.out || fail running demo2 +cat demo2.out +cmp demo2.out demo.expected || fail demo2 output check + +sh demo3a.sh || fail building demo3a +python demo3.py > demo3a.out || fail running demo3a +cat demo3a.out +cmp demo3a.out demo.expected || fail demo3a output check + +sh demo3b.sh || fail building demo3b +python demo3.py > demo3b.out || fail running demo3 variant b +cat demo3b.out +cmp demo3b.out demo.expected || fail demo3b output check + +sh demo3c1.sh || fail building demo3c1 +sh demo3c2.sh || fail building demo3c2 +python demo3.py > demo3c.out || fail running demo3c +cat demo3c.out +cmp demo3c.out demo.expected || fail demo3c output check + +sh demo4.sh || fail building demo4 + +python demo4a.py > demo4a.out || fail running demo4a +cat demo4a.out +cmp demo4a.out demo4.expected || fail demo4a output check + +# This one displays an error which we capture and verify +python demo4a1.py 2>demo4a1.err 1>demo4a1.out || true +test -s demo4a1.out && fail demo4a1 stdout check +# Do output comparison without checking line numbers in trace +cat demo4a1.err \ + | sed -r \ + -e "s@${PYXB_ROOT}@PYXB_ROOT@g" \ + -e 's@line [0-9]+@line #@' \ + > demo4a1.errc +cmp demo4a1.errc demo4a1.expected || fail demo4a1 error check + +python demo4a2.py > demo4a2.out || fail running demo4a2 +cat demo4a2.out +cmp demo4a2.out demo4.expected || fail demo4a2 output check + +sh demo4b.sh || fail building demo4b +python demo4b.py > demo4b.out || fail running demo4b +cat demo4b.out +cmp demo4b.out demo4.expected || fail demo4b output check + +# 4c disables validation on output, so comparison is not +# reliable. +for dc in 4c1 4c2 4c3 ; do + python demo${dc}.py | xmllint --format - > demo${dc}.out + cmp demo${dc}.out demo${dc}.expected || fail demo${dc} output check +done +python badcontent.py > badcontent.out || fail running badcontent +cat badcontent.out +cmp badcontent.out badcontent.expected || fail badcontent output check + +passed diff --git a/examples/ndfd/showreq.expected b/examples/ndfd/showreq.expected new file mode 100644 index 00000000..6e8735cf --- /dev/null +++ b/examples/ndfd/showreq.expected @@ -0,0 +1,24 @@ +PS None +ndfdXMLBinding +ndfdXMLPortType + +Returns National Weather Service digital weather forecast data + + +{https://graphical.weather.gov/xml/DWMLgen/wsdl/ndfdXML.wsdl}NDFDgenRequest +{https://graphical.weather.gov/xml/DWMLgen/wsdl/ndfdXML.wsdl}NDFDgenRequest + + + + + + + +uri:DWMLgen None +latitude latitude +longitude longitude +product product +startTime startTime +endTime endTime +Unit Unit +weatherParameters weatherParameters diff --git a/examples/ndfd/showreq.py b/examples/ndfd/showreq.py index e3ed499e..6cc2b6d7 100644 --- a/examples/ndfd/showreq.py +++ b/examples/ndfd/showreq.py @@ -13,7 +13,7 @@ dom = xml.dom.minidom.parse(open('NDFDgen.xml', 'rb')) body_dom = dom.documentElement.firstChild.nextSibling.firstChild.nextSibling -print(body_dom) +#print(body_dom) # contains varying text in object description # Service interface types import ndfd @@ -30,19 +30,19 @@ port_type = spec.portType[0] print(port_type.name) bop = binding.operationMap()[body_dom.localName] -print(bop.toxml("utf-8")) +print(bop.toxml("utf-8").decode('utf-8')) pop = port_type.operationMap()[body_dom.localName] -print(pop.toxml("utf-8")) +print(pop.toxml("utf-8").decode('utf-8')) input = pop.input -print(input.toxml("utf-8")) +print(input.toxml("utf-8").decode('utf-8')) print(type(input)) print(input.message) im_en = input.message print(im_en) msg = im_en.message() -print(msg) +#print(msg) # contains varying text in object description for p in msg.part: - print(p.toxml("utf-8")) + print(p.toxml("utf-8").decode('utf-8')) msg_ns = pyxb.namespace.NamespaceForURI(body_dom.namespaceURI) print('%s %s' % (body_dom.namespaceURI, msg_ns)) diff --git a/examples/ndfd/test.sh b/examples/ndfd/test.sh index 899ffba4..a9da8269 100755 --- a/examples/ndfd/test.sh +++ b/examples/ndfd/test.sh @@ -1,5 +1,20 @@ +#!/bin/sh + +: ${PYXB_TEST_ROOT:=${PYXB_ROOT}/tests} +. ${PYXB_TEST_ROOT}/support.sh + PYXB_ARCHIVE_PATH="&pyxb/bundles/wssplat//" export PYXB_ARCHIVE_PATH -sh genbindings.sh \ - && python showreq.py \ - && python forecast.py + +sh genbindings.sh || fail generating bindings + +python showreq.py > showreq.out || fail showreq +cmp showreq.out showreq.expected || fail request comparison +# The NWS servers may throttle this; expect it to take no more than 30 +# s +echo "Wait for forecast to be retrieved..." +date +python forecast.py > forecast.out || fail forecast +date +cat forecast.out +passed diff --git a/examples/tmsxtvd/xtest.sh b/examples/tmsxtvd/disabled-test.sh similarity index 100% rename from examples/tmsxtvd/xtest.sh rename to examples/tmsxtvd/disabled-test.sh diff --git a/examples/ucum/test.expected b/examples/ucum/test.expected new file mode 100644 index 00000000..3f68111a --- /dev/null +++ b/examples/ucum/test.expected @@ -0,0 +1,511 @@ +m + prints as: m +s + prints as: s +g + prints as: g +rad + prints as: rad +K + prints as: K +C + prints as: C +cd + prints as: cd +10* as 10 times 1 + prints as: 10 +10^ as 10 times 1 + prints as: 10 +[pi] as 3.1415926535897932384626433832795028841971693993751058209749445923 times 1 + prints as: π +% alias 10*-2 + prints as: % +[ppth] alias 10*-3 + prints as: ppth +[ppm] alias 10*-6 + prints as: ppm +[ppb] alias 10*-9 + prints as: ppb +[pptr] alias 10*-12 + prints as: pptr +mol as 6.0221367 times 10*23 + prints as: mol +sr alias rad2 + prints as: sr +Hz alias s-1 + prints as: Hz +N alias kg.m/s2 + prints as: N +Pa alias N/m2 + prints as: Pa +J alias N.m + prints as: J +W alias J/s + prints as: W +A alias C/s + prints as: A +V alias J/C + prints as: V +F alias C/V + prints as: F +Ohm alias V/A + prints as: Ω +S alias Ohm-1 + prints as: S +Wb alias V.s + prints as: Wb +Cel as value cel(1 K) + prints as: °C +T alias Wb/m2 + prints as: T +H alias Wb/A + prints as: H +lm alias cd.sr + prints as: lm +lx alias lm/m2 + prints as: lx +Bq alias s-1 + prints as: Bq +Gy alias J/kg + prints as: Gy +Sv alias J/kg + prints as: Sv +gon as 0.9 times deg + prints as: □g +deg as 2 times [pi].rad/360 + prints as: ° +' alias deg/60 + prints as: ' +'' alias '/60 + prints as: '' +l alias dm3 + prints as: l +L alias l + prints as: L +ar as 100 times m2 + prints as: a +min as 60 times s + prints as: min +h as 60 times min + prints as: h +d as 24 times h + prints as: d +a_t as 365.24219 times d + prints as: at +a_j as 365.25 times d + prints as: aj +a_g as 365.2425 times d + prints as: ag +a alias a_j + prints as: a +wk as 7 times d + prints as: wk +mo_s as 29.53059 times d + prints as: mos +mo_j alias a_j/12 + prints as: moj +mo_g alias a_g/12 + prints as: mog +mo alias mo_j + prints as: mo +t as 1E+3 times kg + prints as: t +bar as 1E+5 times Pa + prints as: bar +u as 1.6605402E-24 times g + prints as: u +eV alias [e].V + prints as: eV +AU as 149597.870691 times Mm + prints as: AU +pc as 3.085678E+16 times m + prints as: pc +[c] as 299792458 times m/s + prints as: c +[h] as 6.6260755E-34 times J.s + prints as: h +[k] as 1.380658E-23 times J/K + prints as: k +[eps_0] as 8.854187817E-12 times F/m + prints as: ε0 +[mu_0] alias 4.[pi].10*-7.N/A2 + prints as: μ0 +[e] as 1.60217733E-19 times C + prints as: e +[m_e] as 9.1093897E-28 times g + prints as: me +[m_p] as 1.6726231E-24 times g + prints as: mp +[G] as 6.67259E-11 times m3.kg-1.s-2 + prints as: G +[g] as 9.80665 times m/s2 + prints as: gn +atm as 101325 times Pa + prints as: atm +[ly] alias [c].a_j + prints as: l.y. +gf alias g.[g] + prints as: gf +[lbf_av] alias [lb_av].[g] + prints as: lbf +Ky alias cm-1 + prints as: K +Gal alias cm/s2 + prints as: Gal +dyn alias g.cm/s2 + prints as: dyn +erg alias dyn.cm + prints as: erg +P alias dyn.s/cm2 + prints as: P +Bi as 10 times A + prints as: Bi +St alias cm2/s + prints as: St +Mx as 1E-8 times Wb + prints as: Mx +G as 0.0001 times T + prints as: Gs +Oe as 250 times /[pi].A/m + prints as: Oe +Gb alias Oe.cm + prints as: Gb +sb alias cd/cm2 + prints as: sb +Lmb alias cd/cm2/[pi] + prints as: L +ph as 0.0001 times lx + prints as: ph +Ci as 3.7E+10 times Bq + prints as: Ci +R as 0.000258 times C/kg + prints as: R +RAD as 100 times erg/g + prints as: RAD +REM alias RAD + prints as: REM +[in_i] as 2.54 times cm + prints as: in +[ft_i] as 12 times [in_i] + prints as: ft +[yd_i] as 3 times [ft_i] + prints as: yd +[mi_i] as 5280 times [ft_i] + prints as: mi +[fth_i] as 6 times [ft_i] + prints as: fth +[nmi_i] as 1852 times m + prints as: n.mi +[kn_i] alias [nmi_i]/h + prints as: knot +[sin_i] alias [in_i]2 +[sft_i] alias [ft_i]2 +[syd_i] alias [yd_i]2 +[cin_i] alias [in_i]3 +[cft_i] alias [ft_i]3 +[cyd_i] alias [yd_i]3 + prints as: cu.yd +[bf_i] as 144 times [in_i]3 +[cr_i] as 128 times [ft_i]3 +[mil_i] as 0.001 times [in_i] + prints as: mil +[cml_i] alias [pi]/4.[mil_i]2 + prints as: circ.mil +[hd_i] as 4 times [in_i] + prints as: hd +[ft_us] as 1200 times m/3937 + prints as: ftus +[yd_us] as 3 times [ft_us] +[in_us] alias [ft_us]/12 +[rd_us] as 16.5 times [ft_us] +[ch_us] as 4 times [rd_us] +[lk_us] alias [ch_us]/100 +[rch_us] as 100 times [ft_us] +[rlk_us] alias [rch_us]/100 +[fth_us] as 6 times [ft_us] +[fur_us] as 40 times [rd_us] +[mi_us] as 8 times [fur_us] +[acr_us] as 160 times [rd_us]2 +[srd_us] alias [rd_us]2 +[smi_us] alias [mi_us]2 +[sct] alias [mi_us]2 +[twp] as 36 times [sct] +[mil_us] as 0.001 times [in_us] +[in_br] as 2.539998 times cm +[ft_br] as 12 times [in_br] +[rd_br] as 16.5 times [ft_br] +[ch_br] as 4 times [rd_br] +[lk_br] alias [ch_br]/100 +[fth_br] as 6 times [ft_br] +[pc_br] as 2.5 times [ft_br] +[yd_br] as 3 times [ft_br] +[mi_br] as 5280 times [ft_br] +[nmi_br] as 6080 times [ft_br] +[kn_br] alias [nmi_br]/h +[acr_br] as 4840 times [yd_br]2 +[gal_us] as 231 times [in_i]3 +[bbl_us] as 42 times [gal_us] +[qt_us] alias [gal_us]/4 +[pt_us] alias [qt_us]/2 +[gil_us] alias [pt_us]/4 +[foz_us] alias [gil_us]/4 + prints as: oz fl +[fdr_us] alias [foz_us]/8 +[min_us] alias [fdr_us]/60 +[crd_us] as 128 times [ft_i]3 +[bu_us] as 2150.42 times [in_i]3 +[gal_wi] alias [bu_us]/8 +[pk_us] alias [bu_us]/4 +[dqt_us] alias [pk_us]/8 +[dpt_us] alias [dqt_us]/2 +[tbs_us] alias [foz_us]/2 +[tsp_us] alias [tbs_us]/3 +[cup_us] as 16 times [tbs_us] +[foz_m] as 30 times mL + prints as: oz fl +[cup_m] as 240 times mL +[tsp_m] as 5 times mL +[tbs_m] as 15 times mL +[gal_br] as 4.54609 times l +[pk_br] as 2 times [gal_br] +[bu_br] as 4 times [pk_br] +[qt_br] alias [gal_br]/4 +[pt_br] alias [qt_br]/2 +[gil_br] alias [pt_br]/4 +[foz_br] alias [gil_br]/5 +[fdr_br] alias [foz_br]/8 +[min_br] alias [fdr_br]/60 +[gr] as 64.79891 times mg +[lb_av] as 7000 times [gr] + prints as: lb +[oz_av] alias [lb_av]/16 + prints as: oz +[dr_av] alias [oz_av]/16 +[scwt_av] as 100 times [lb_av] +[lcwt_av] as 112 times [lb_av] +[ston_av] as 20 times [scwt_av] +[lton_av] as 20 times [lcwt_av] +[stone_av] as 14 times [lb_av] +[pwt_tr] as 24 times [gr] +[oz_tr] as 20 times [pwt_tr] +[lb_tr] as 12 times [oz_tr] +[sc_ap] as 20 times [gr] +[dr_ap] as 3 times [sc_ap] +[oz_ap] as 8 times [dr_ap] +[lb_ap] as 12 times [oz_ap] +[oz_m] as 28 times g +[lne] alias [in_i]/12 +[pnt] alias [lne]/6 +[pca] as 12 times [pnt] +[pnt_pr] as 0.013837 times [in_i] +[pca_pr] as 12 times [pnt_pr] +[pied] as 32.48 times cm +[pouce] alias [pied]/12 +[ligne] alias [pouce]/12 +[didot] alias [ligne]/6 +[cicero] as 12 times [didot] +[degF] as value degf(5 K/9) + prints as: °F +[degR] as 5 times K/9 + prints as: °R +[degRe] as value degre(5 K/4) + prints as: °Ré +cal_[15] as 4.18580 times J + prints as: cal15°C +cal_[20] as 4.18190 times J + prints as: cal20°C +cal_m as 4.19002 times J + prints as: calm +cal_IT as 4.1868 times J + prints as: calIT +cal_th as 4.184 times J + prints as: calth +cal alias cal_th + prints as: cal +[Cal] alias kcal_th + prints as: Cal +[Btu_39] as 1.05967 times kJ + prints as: Btu39°F +[Btu_59] as 1.05480 times kJ + prints as: Btu59°F +[Btu_60] as 1.05468 times kJ + prints as: Btu60°F +[Btu_m] as 1.05587 times kJ + prints as: Btum +[Btu_IT] as 1.05505585262 times kJ + prints as: BtuIT +[Btu_th] as 1.054350 times kJ + prints as: Btuth +[Btu] alias [Btu_th] + prints as: btu +[HP] as 550 times [ft_i].[lbf_av]/s +tex alias g/km + prints as: tex +[den] alias g/9/km + prints as: den +m[H2O] as 9.80665 times kPa + prints as: m H2O +m[Hg] as 133.3220 times kPa + prints as: m Hg +[in_i'H2O] alias m[H2O].[in_i]/m + prints as: in H2O +[in_i'Hg] alias m[Hg].[in_i]/m + prints as: in Hg +[PRU] alias mm[Hg].s/ml + prints as: P.R.U. +[wood'U] alias mm[Hg].min/L + prints as: Wood U. +[diop] alias /m + prints as: dpt +[p'diop] as value 100tan(1 rad) + prints as: PD +%[slope] as value 100tan(1 rad) + prints as: % +[mesh_i] alias /[in_i] +[Ch] alias mm/3 + prints as: Ch +[drp] alias ml/20 + prints as: drp +[hnsf'U] alias 1 + prints as: HF +[MET] as 3.5 times mL/min/kg + prints as: MET +[hp'_X] as value hpX(1 1) + prints as: X +[hp'_C] as value hpC(1 1) + prints as: C +[hp'_M] as value hpM(1 1) + prints as: M +[hp'_Q] as value hpQ(1 1) + prints as: Q +[hp_X] alias 1 + prints as: X +[hp_C] alias 1 + prints as: C +[hp_M] alias 1 + prints as: M +[hp_Q] alias 1 + prints as: Q +[kp_X] alias 1 + prints as: X +[kp_C] alias 1 + prints as: C +[kp_M] alias 1 + prints as: M +[kp_Q] alias 1 + prints as: Q +eq alias mol + prints as: eq +osm alias mol + prints as: osm +[pH] as value pH(1 mol/l) + prints as: pH +g% alias g/dl + prints as: g% +[S] alias 10*-13.s + prints as: S +[HPF] alias 1 + prints as: HPF +[LPF] as 100 times 1 + prints as: LPF +kat alias mol/s + prints as: kat +U alias umol/min + prints as: U +[iU] alias 1 + prints as: IU +[IU] alias [iU] + prints as: i.U. +[arb'U] alias 1 + prints as: arb. U +[USP'U] alias 1 + prints as: U.S.P. +[GPL'U] alias 1 +[MPL'U] alias 1 +[APL'U] alias 1 +[beth'U] alias 1 +[anti'Xa'U] alias 1 +[todd'U] alias 1 +[dye'U] alias 1 +[smgy'U] alias 1 +[bdsk'U] alias 1 +[ka'U] alias 1 +[knk'U] alias 1 +[mclg'U] alias 1 +[tb'U] alias 1 +[CCID_50] alias 1 + prints as: CCID50 +[TCID_50] alias 1 + prints as: TCID50 +[EID_50] alias 1 + prints as: EID50 +[PFU] alias 1 + prints as: PFU +[FFU] alias 1 + prints as: FFU +[CFU] alias 1 + prints as: CFU +[IR] alias 1 + prints as: IR +[BAU] alias 1 + prints as: BAU +[AU] alias 1 + prints as: AU +[Amb'a'1'U] alias 1 + prints as: Amb a 1 U +[PNU] alias 1 + prints as: PNU +[Lf] alias 1 + prints as: Lf +[D'ag'U] alias 1 +[FEU] alias 1 +[ELU] alias 1 +[EU] alias 1 +Np as value ln(1 1) + prints as: Np +B as value lg(1 1) + prints as: B +B[SPL] as value 2lg(2 10*-5.Pa) + prints as: B(SPL) +B[V] as value 2lg(1 V) + prints as: B(V) +B[mV] as value 2lg(1 mV) + prints as: B(mV) +B[uV] as value 2lg(1 uV) + prints as: B(μV) +B[10.nV] as value 2lg(10 nV) + prints as: B(10 nV) +B[W] as value lg(1 W) + prints as: B(W) +B[kW] as value lg(1 kW) + prints as: B(kW) +st alias m3 + prints as: st +Ao as 0.1 times nm + prints as: Å +b as 100 times fm2 + prints as: b +att alias kgf/cm2 + prints as: at +mho alias S + prints as: mho +[psi] alias [lbf_av]/[in_i]2 + prints as: psi +circ as 2 times [pi].rad + prints as: circ +sph as 4 times [pi].sr + prints as: sph +[car_m] as 0.2 times g + prints as: ctm +[car_Au] alias /24 + prints as: ctAu +[smoot] as 67 times [in_i] +[m/s2/Hz^(1/2)] as value sqrt(1 m2/s4/Hz) +bit_s as value ld(1 1) + prints as: bits +bit alias 1 + prints as: bit +By as 8 times bit + prints as: B +Bd alias /s + prints as: Bd diff --git a/examples/ucum/test.sh b/examples/ucum/test.sh index 7a00cb1e..313d3551 100755 --- a/examples/ucum/test.sh +++ b/examples/ucum/test.sh @@ -1,12 +1,17 @@ +#!/bin/sh + +: ${PYXB_TEST_ROOT:=${PYXB_ROOT}/tests} +. ${PYXB_TEST_ROOT}/support.sh + pyxbgen \ -u ucum-essence.xsd \ - -m ucum + -m ucum \ + || fail generating bindings + if [ ! -f ucum-essence.xml ] ; then wget http://unitsofmeasure.org/ucum-essence.xml fi -# This allows this script to run under the autotest environment, where -# output is sent to a file. -export PYTHONIOENCODING='utf-8' - -python showunits.py +python showunits.py > test.out || fail running +cmp test.out test.expected || fail output mismatch +passed diff --git a/examples/unicode_jp/README.txt b/examples/unicode_jp/README.txt index 1950ffe0..5935fbe6 100644 --- a/examples/unicode_jp/README.txt +++ b/examples/unicode_jp/README.txt @@ -66,6 +66,13 @@ the bindings. Many thanks to Hiroaki Itoh for providing the schemas, example document, and romanization code. +If you are interested in other languages, consider replacing the +ConvertJPIdentifier() function in the modified pyxbgen script with one +that uses unidecode: https://pypi.python.org/pypi/Unidecode + +See this comment for further details: +https://sourceforge.net/p/pyxb/discussion/956708/thread/5246b205/#1c7f + Note: Because the package depends on OpenGIS, and OpenGIS bindings are no longer provided in the PyXB distribution, you should generate these bindings first. If they are missing, the test script will emit a warning and PyXB diff --git a/examples/unicode_jp/test.sh b/examples/unicode_jp/test.sh index 691c4b74..2231aa19 100755 --- a/examples/unicode_jp/test.sh +++ b/examples/unicode_jp/test.sh @@ -1,9 +1,7 @@ #!/bin/sh -fail () { - echo 1>&2 "${test_name} FAILED: ${@}" - exit 1 -} +: ${PYXB_TEST_ROOT:=${PYXB_ROOT}/tests} +. ${PYXB_TEST_ROOT}/support.sh # Because this is an OpenGIS application, the OpenGIS bundle must be # made available during binding generation. OpenGIS also depends @@ -27,14 +25,12 @@ opengis bundle directory. EOText fi -# This allows this script to run under the autotest environment, where -# output is sent to a file. -export PYTHONIOENCODING='utf-8' - -rm fgd_gml.* +rm -f fgd_gml.* # A customized pyxbgen is required to do the translation ./pyxbgen_jp \ - --schema-location=data/shift_jis/FGD_GMLSchema.xsd --module=fgd_gml + --schema-location=data/shift_jis/FGD_GMLSchema.xsd --module=fgd_gml \ + || fail generating bindings # Make sure it worked -python check.py +python check.py || fail check +passed diff --git a/examples/weather/README.txt b/examples/weather/README.txt index a3f430e3..896254c7 100644 --- a/examples/weather/README.txt +++ b/examples/weather/README.txt @@ -1,3 +1,7 @@ +Updated 2017-05-12: This service is no longer available. Similar +services are still hosted by this company but require a developer key to +access. + A free weather service at http://ws.cdyne.com/WeatherWS/Weather.asmx Use genbindings.sh to retrieve the wsdl and generate the bindings for the diff --git a/examples/weather/test.sh b/examples/weather/disabled-test.sh similarity index 100% rename from examples/weather/test.sh rename to examples/weather/disabled-test.sh diff --git a/examples/xhtml/test.sh b/examples/xhtml/test.sh index 59507151..5b2aea25 100755 --- a/examples/xhtml/test.sh +++ b/examples/xhtml/test.sh @@ -1,20 +1,18 @@ -TEST_URI=http://www.w3.org/People/mimasa/test/xhtml/media-types/test.xhtml +#!/bin/sh -aout="${0}" +: ${PYXB_TEST_ROOT:=${PYXB_ROOT}/tests} +. ${PYXB_TEST_ROOT}/support.sh -fail () { - echo 1>&2 "${aout} FAILED: ${@}" - exit 1 -} +TEST_URI=http://www.w3.org/People/mimasa/test/xhtml/media-types/test.xhtml if [ ! -f in.xhtml ] ; then - wget -O in.xhtml ${TEST_URI} || fail Unable to retrieve test document + wget -O in.xhtml ${TEST_URI} || fail retrieving document fi -python rewrite.py || fail Unable to rewrite test document +python rewrite.py || fail rewriting document xmllint --format in.xhtml > inf.xhtml xmllint --format out.xhtml > outf.xhtml -diff -uw inf.xhtml outf.xhtml > deltas +diff -uw inf.xhtml outf.xhtml > deltas || true # Need to manually validate that outf.xhtml and in.xhtml are about the # same. The rewrite does not preserve the order of attributes in the @@ -23,8 +21,5 @@ echo "See deltas for differences" # Test most primitive generation of documents rm -f genout.xhtml -python generate.py \ - && diff expout.xhtml genout.xhtml \ - || fail generate did not match expected - -echo "Passed XHTML tests" +python generate.py > genout.xhtml || fail running +passed diff --git a/examples/xsdprimer/demo.expected b/examples/xsdprimer/demo.expected new file mode 100644 index 00000000..ad21113e --- /dev/null +++ b/examples/xsdprimer/demo.expected @@ -0,0 +1,4 @@ +Robert Smith is sending Mary Jones 1 thing(s): + Quantity 1 of Lapis necklace at $99.95 +Too many: Type maxExclusive constraint violated by value 100 +Increased quantity to 10 diff --git a/examples/xsdprimer/demo.py b/examples/xsdprimer/demo.py index 34e5f519..6c018156 100644 --- a/examples/xsdprimer/demo.py +++ b/examples/xsdprimer/demo.py @@ -9,14 +9,14 @@ order = ipo.CreateFromDOM(xml.dom.minidom.parse(io.BytesIO(xmld)).documentElement) -print('%s is sending %s %d thing(s):' % (order.billTo().name(), order.shipTo().name(), len(order.items().item()))) -for item in order.items().item(): - print(' Quantity %d of%s at $%s' % (item.quantity(), item.productName(), item.USPrice())) +print('%s is sending %s %d thing(s):' % (order.billTo.name, order.shipTo.name, len(order.items.item))) +for item in order.items.item: + print(' Quantity %d of %s at $%s' % (item.quantity, item.productName, item.USPrice)) # Give Mary more try: - item.setQuantity(100) + item.quantity = 100; except pyxb.SimpleTypeValueError as e: print('Too many: %s' % (e,)) - item.setQuantity(10) -print('Increased quantity to %d' % (item.quantity(),)) + item.quantity = 10; +print('Increased quantity to %d' % (item.quantity,)) diff --git a/examples/xsdprimer/ipo.py b/examples/xsdprimer/ipo.py index bfb82a21..0b5fe5ea 100644 --- a/examples/xsdprimer/ipo.py +++ b/examples/xsdprimer/ipo.py @@ -10,6 +10,6 @@ class USAddress (raw_ipo.USAddress): def __str__ (self): return six.u('''%s %s -%s %s, %s''') % (self.name(), self.street(), self.city(), self.state(), self.zip()) +%s %s, %s''') % (self.name, self.street, self.city, self.state, self.zip) pass raw_ipo.USAddress._SetSupersedingClass(USAddress) diff --git a/examples/xsdprimer/saxdemo.py b/examples/xsdprimer/saxdemo.py index dfe4e8c2..e5b156ae 100644 --- a/examples/xsdprimer/saxdemo.py +++ b/examples/xsdprimer/saxdemo.py @@ -5,9 +5,9 @@ import ipo def ShowOrder (order): - print('%s is sending %s %d thing(s):' % (order.billTo().name(), order.shipTo().name(), len(order.items().item()))) - for item in order.items().item(): - print(' Quantity %d of %s at $%s' % (item.quantity(), item.productName(), item.USPrice())) + print('%s is sending %s %d thing(s):' % (order.billTo.name, order.shipTo.name, len(order.items.item))) + for item in order.items.item: + print(' Quantity %d of %s at $%s' % (item.quantity, item.productName, item.USPrice)) if False: import pyxb.utils.domutils diff --git a/examples/xsdprimer/test.sh b/examples/xsdprimer/test.sh new file mode 100755 index 00000000..9337978b --- /dev/null +++ b/examples/xsdprimer/test.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +: ${PYXB_TEST_ROOT:=${PYXB_ROOT}/tests} +. ${PYXB_TEST_ROOT}/support.sh + +sh genbindings.sh || fail generating bindings +python demo.py > demo.out || fail running demo +cmp demo.out demo.expected || fail demo output mismatch + +passed diff --git a/maintainer/genbundles b/maintainer/genbundles index 0f1fed38..bf1dbcc7 100755 --- a/maintainer/genbundles +++ b/maintainer/genbundles @@ -11,7 +11,7 @@ # previous bundles are made available during the build. BUNDLES="common wssplat saml20 dc" -AUX_BUNDLES="opengis ecma376" +AUX_BUNDLES="opengis ecma376 reqif" if [ 0 -lt $# ] ; then BUNDLES=$(echo ${@} | sed -e "s/@/${BUNDLES}/" -e "s/%/${AUX_BUNDLES}/") fi diff --git a/pyxb/__init__.py b/pyxb/__init__.py index 198caafe..5e6dd113 100644 --- a/pyxb/__init__.py +++ b/pyxb/__init__.py @@ -61,7 +61,7 @@ def __init__ (self, *args, **kw): if issubclass(self.__class__.mro()[-2], ( list, dict )): super(cscRoot, self).__init__(*args) -__version__ = '1.2.5' +__version__ = '1.2.6' """The version of PyXB""" __url__ = 'http://pyxb.sourceforge.net' diff --git a/pyxb/binding/basis.py b/pyxb/binding/basis.py index 79db86fb..f624e0e6 100644 --- a/pyxb/binding/basis.py +++ b/pyxb/binding/basis.py @@ -23,6 +23,7 @@ from pyxb.utils import domutils, utility, six import pyxb.namespace from pyxb.namespace.builtin import XMLSchema_instance as XSI +import decimal _log = logging.getLogger(__name__) @@ -428,6 +429,10 @@ def _CompatibleValue (cls, value, **kw): rv = cls.Factory(value) if isinstance(rv, simpleTypeDefinition) and (rv == value): return rv + # Python decimal instances do not compare equal to float values; + # test whether the string representation is equal instead. + if isinstance(rv, decimal.Decimal) and (str(rv) == str(value)): + return rv if isinstance(rv, complexTypeDefinition) and (rv.value() == value): return rv @@ -700,6 +705,14 @@ class _RepresentAsXsdLiteral_mixin (pyxb.cscRoot): e.g. duration, decimal, and any of the date/time types.""" pass +class _NoNullaryNonNillableNew_mixin (pyxb.cscRoot): + """Marker class indicating that a simple data type cannot construct + a value from XML through an empty string. + + This class should appear immediately L{simpleTypeDefinition} (or whatever + inherits from L{simpleTypeDefinition} in cases where it applies.""" + pass + class simpleTypeDefinition (_TypeBinding_mixin, utility._DeconflictSymbols_mixin, _DynamicCreate_mixin): """L{simpleTypeDefinition} is a base class that is part of the hierarchy of any class that represents the Python datatype for a @@ -881,11 +894,16 @@ def __new__ (cls, *args, **kw): kw.pop('_element', None) kw.pop('_fallback_namespace', None) kw.pop('_apply_attributes', None) - kw.pop('_nil', None) - # ConvertArguments will remove _element and _apply_whitespace_facet - dom_node = kw.get('_dom_node') + is_nil = kw.pop('_nil', None) + # ConvertArguments will remove _dom_node, _element, and + # _apply_whitespace_facet, and it will set _from_xml. args = cls._ConvertArguments(args, kw) - kw.pop('_from_xml', dom_node is not None) + from_xml = kw.pop('_from_xml', False) + if ((0 == len(args)) + and from_xml + and not is_nil + and issubclass(cls, _NoNullaryNonNillableNew_mixin)): + raise pyxb.SimpleTypeValueError(cls, args); kw.pop('_location', None) assert issubclass(cls, _TypeBinding_mixin) try: @@ -1631,7 +1649,8 @@ def compatibleValue (self, value, **kw): if not isinstance(value, collections.Iterable): raise pyxb.SimplePluralValueError(self.typeDefinition(), value) return [ self.compatibleValue(_v) for _v in value ] - if self.__fixed and (value != self.__defaultValue): + compValue = self.typeDefinition()._CompatibleValue(value, **kw); + if self.__fixed and (compValue != self.__defaultValue): raise pyxb.ElementChangeError(self, value) if isinstance(value, _TypeBinding_mixin) and (value._element() is not None) and value._element().substitutesFor(self): return value @@ -1640,7 +1659,7 @@ def compatibleValue (self, value, **kw): if isinstance(value, utility.Locatable_mixin): location = value._location() raise pyxb.AbstractElementError(self, location, value) - return self.typeDefinition()._CompatibleValue(value, **kw) + return compValue @classmethod def CreateDOMBinding (cls, node, element_binding, **kw): diff --git a/pyxb/binding/datatypes.py b/pyxb/binding/datatypes.py index 05c1fb72..2aa1664e 100644 --- a/pyxb/binding/datatypes.py +++ b/pyxb/binding/datatypes.py @@ -94,7 +94,7 @@ def XsdValueLength (cls, value): # It is illegal to subclass the bool type in Python, so we subclass # int instead. @six.python_2_unicode_compatible -class boolean (basis.simpleTypeDefinition, six.int_type): +class boolean (basis.simpleTypeDefinition, six.int_type, basis._NoNullaryNonNillableNew_mixin): """XMLSchema datatype U{boolean}.""" _XsdBaseType = anySimpleType _ExpandedName = pyxb.namespace.XMLSchema.createExpandedName('boolean') @@ -126,7 +126,7 @@ def __new__ (cls, *args, **kw): _PrimitiveDatatypes.append(boolean) -class decimal (basis.simpleTypeDefinition, python_decimal.Decimal, basis._RepresentAsXsdLiteral_mixin): +class decimal (basis.simpleTypeDefinition, python_decimal.Decimal, basis._RepresentAsXsdLiteral_mixin, basis._NoNullaryNonNillableNew_mixin): """XMLSchema datatype U{decimal}. This class uses Python's L{decimal.Decimal} class to support (by @@ -184,7 +184,7 @@ def XsdLiteral (cls, value): _PrimitiveDatatypes.append(decimal) -class _fp (basis.simpleTypeDefinition, six.float_type): +class _fp (basis.simpleTypeDefinition, six.float_type, basis._NoNullaryNonNillableNew_mixin): _XsdBaseType = anySimpleType @classmethod @@ -246,70 +246,79 @@ def durationData (self): def __new__ (cls, *args, **kw): args = cls._ConvertArguments(args, kw) have_kw_update = False - if not kw.get('_nil'): - if 0 == len(args): - raise SimpleTypeValueError(cls, args) - text = args[0] + negative_duration = False if kw.get('_nil'): data = dict(zip(cls.__PythonFields, len(cls.__PythonFields) * [0,])) - negative_duration = False - elif isinstance(text, six.string_types): - match = cls.__Lexical_re.match(text) - if match is None: - raise SimpleTypeValueError(cls, text) - match_map = match.groupdict() - if 'T' == match_map.get('Time'): - # Can't have T without additional time information - raise SimpleTypeValueError(cls, text) - - negative_duration = ('-' == match_map.get('neg')) - - fractional_seconds = 0.0 - if match_map.get('fracsec') is not None: - fractional_seconds = six.float_type('0%s' % (match_map['fracsec'],)) - usec = six.int_type(1000000 * fractional_seconds) - if negative_duration: - kw['microseconds'] = - usec - else: - kw['microseconds'] = usec - else: - # Discard any bogosity passed in by the caller - kw.pop('microsecond', None) - - data = { } - for fn in cls.__XSDFields: - v = match_map.get(fn, 0) - if v is None: - v = 0 - data[fn] = six.int_type(v) - if fn in cls.__PythonFields: - if negative_duration: - kw[fn] = - data[fn] - else: - kw[fn] = data[fn] - data['seconds'] += fractional_seconds - have_kw_update = True - elif isinstance(text, cls): - data = text.durationData().copy() - negative_duration = text.negativeDuration() - elif isinstance(text, datetime.timedelta): - data = { 'days' : text.days, - 'seconds' : text.seconds + (text.microseconds / 1000000.0) } - negative_duration = (0 > data['days']) - if negative_duration: - if 0.0 == data['seconds']: - data['days'] = - data['days'] - else: - data['days'] = 1 - data['days'] - data['seconds'] = 24 * 60 * 60.0 - data['seconds'] - data['minutes'] = 0 - data['hours'] = 0 - elif isinstance(text, six.integer_types) and (1 < len(args)): + elif 0 == len(args): + if kw.get('_from_xml'): + raise SimpleTypeValueError(cls, args) + data = dict(zip(cls.__PythonFields, len(cls.__PythonFields) * [0,])) + elif 1 < len(args): + if kw.get('_from_xml'): + raise SimpleTypeValueError(cls, args) # Apply the arguments as in the underlying Python constructor data = dict(zip(cls.__PythonFields[:len(args)], args)) - negative_duration = False else: - raise SimpleTypeValueError(cls, text) + text = args[0]; + if isinstance(text, six.string_types): + match = cls.__Lexical_re.match(text) + if match is None: + raise SimpleTypeValueError(cls, text) + match_map = match.groupdict() + if 'T' == match_map.get('Time'): + # Can't have T without additional time information + raise SimpleTypeValueError(cls, text) + + negative_duration = ('-' == match_map.get('neg')) + + fractional_seconds = 0.0 + if match_map.get('fracsec') is not None: + fractional_seconds = six.float_type('0%s' % (match_map['fracsec'],)) + usec = six.int_type(1000000 * fractional_seconds) + if negative_duration: + kw['microseconds'] = - usec + else: + kw['microseconds'] = usec + else: + # Discard any bogosity passed in by the caller + kw.pop('microsecond', None) + + data = { } + for fn in cls.__XSDFields: + v = match_map.get(fn, 0) + if v is None: + v = 0 + data[fn] = six.int_type(v) + if fn in cls.__PythonFields: + if negative_duration: + kw[fn] = - data[fn] + else: + kw[fn] = data[fn] + data['seconds'] += fractional_seconds + have_kw_update = True + elif kw.get('_from_xml'): + raise SimpleTypeValueError(cls, args) + elif isinstance(text, cls): + data = text.durationData().copy() + negative_duration = text.negativeDuration() + elif isinstance(text, datetime.timedelta): + data = { 'days' : text.days, + 'seconds' : text.seconds + (text.microseconds / 1000000.0) } + negative_duration = (0 > data['days']) + if negative_duration: + if 0.0 == data['seconds']: + data['days'] = - data['days'] + else: + data['days'] = 1 - data['days'] + data['seconds'] = 24 * 60 * 60.0 - data['seconds'] + data['minutes'] = 0 + data['hours'] = 0 + elif isinstance(text, six.integer_types): + # Apply the arguments as in the underlying Python constructor + data = dict(zip(cls.__PythonFields[:len(args)], args)) + negative_duration = False + else: + raise SimpleTypeValueError(cls, text) if not have_kw_update: rem_time = data.pop('seconds', 0) if (0 != (rem_time % 1)): @@ -354,7 +363,7 @@ def XsdLiteral (cls, value): time_elts.append('%d%s' % (v, k[0].upper())) v = value.__durationData.get('seconds', 0) if 0 != v: - time_elts.append('%gS' % (v,)) + time_elts.append(('%f' % (v,)).rstrip('0').rstrip('.') + 'S') if 0 < len(time_elts): elts.append('T') elts.extend(time_elts) @@ -446,6 +455,11 @@ def _SetKeysFromPython (cls, python_value, kw, fields): def __reduce__ (self): return (self.__class__, (self.xsdLiteral(),)) + # In Python 3.6 datetime started using __reduce_ex__ which is + # higher priority. Override it too. + def __reduce_ex__ (self, protocol): + return (self.__class__, (self.xsdLiteral(),)) + @classmethod def _AdjustForTimezone (cls, kw): """Update datetime keywords to account for timezone effects. @@ -516,6 +530,10 @@ def __new__ (cls, *args, **kw): ctor_kw = { } if kw.get('_nil'): ctor_kw = { 'year': 1900, 'month': 1, 'day': 1 } + elif 0 == len(args): + if kw.get('_from_xml'): + raise SimpleTypeValueError(cls, args) + ctor_kw = { 'year': 1900, 'month': 1, 'day': 1 } elif 1 == len(args): value = args[0] if isinstance(value, six.string_types): @@ -590,7 +608,12 @@ class time (_PyXBDateTime_base, datetime.time): def __new__ (cls, *args, **kw): args = cls._ConvertArguments(args, kw) ctor_kw = { } - if 1 <= len(args): + if kw.get('_nil'): + pass + elif 0 == len(args): + if kw.get('_from_xml'): + raise SimpleTypeValueError(cls, args) + else: value = args[0] if isinstance(value, six.string_types): ctor_kw.update(cls._LexicalToKeywords(value)) @@ -1162,7 +1185,7 @@ class ENTITIES (basis.STD_list): _ItemType = ENTITY _ListDatatypes.append(ENTITIES) -class integer (basis.simpleTypeDefinition, six.long_type): +class integer (basis.simpleTypeDefinition, six.long_type, basis._NoNullaryNonNillableNew_mixin): """XMLSchema datatype U{integer}.""" _XsdBaseType = decimal _ExpandedName = pyxb.namespace.XMLSchema.createExpandedName('integer') @@ -1188,7 +1211,7 @@ class long (integer): _ExpandedName = pyxb.namespace.XMLSchema.createExpandedName('long') _DerivedDatatypes.append(long) -class int (basis.simpleTypeDefinition, six.int_type): +class int (basis.simpleTypeDefinition, six.int_type, basis._NoNullaryNonNillableNew_mixin): """XMLSchema datatype U{int}.""" _XsdBaseType = long _ExpandedName = pyxb.namespace.XMLSchema.createExpandedName('int') diff --git a/pyxb/binding/generate.py b/pyxb/binding/generate.py index cb44b822..a05b58eb 100644 --- a/pyxb/binding/generate.py +++ b/pyxb/binding/generate.py @@ -1128,7 +1128,10 @@ def _SetNameWithAccessors (component, container, is_plural, binding_module, nsm, use_map = component._templateMap() class_unique = nsm.uniqueInClass(container) assert isinstance(component, xs.structures._ScopedDeclaration_mixin) - unique_name = utility.PrepareIdentifier(component.expandedName().localName(), class_unique) + unique_name = component.expandedName().localName() + if component.overridesParentScope(): + class_unique.discard(component.overriddenDeclaration().uniqueNameInBinding()) + unique_name = utility.PrepareIdentifier(unique_name, class_unique) use_map['id'] = unique_name use_map['inspector'] = unique_name use_map['mutator'] = utility.PrepareIdentifier('set' + unique_name[0].upper() + unique_name[1:], class_unique) @@ -1136,6 +1139,7 @@ def _SetNameWithAccessors (component, container, is_plural, binding_module, nsm, assert component._scope() == container assert component.nameInBinding() is None, 'Use %s but binding name %s for %s' % (use_map['use'], component.nameInBinding(), component.expandedName()) component.setNameInBinding(use_map['use']) + component.setUniqueNameInBinding(use_map['id']) key_name = six.u('%s_%s_%s') % (six.text_type(nsm.namespace()), container.nameInBinding(), component.expandedName()) use_map['key'] = utility.PrepareIdentifier(key_name, class_unique, private=True) use_map['qname'] = six.text_type(component.expandedName()) @@ -1932,13 +1936,9 @@ def setSchemaRoot (self, schema_root): def schemaStrippedPrefix (self): """Optional string that is stripped from the beginning of - schemaLocation values before loading from them. - - This applies only to the values of schemaLocation attributes - in C{import} and C{include} elements. Its purpose is to - convert absolute schema locations into relative ones to allow - offline processing when all schema are available in a local - directory. See C{schemaRoot}. + schemaLocation values before loading from them. This now + applies only to URIs specified on the command line so is + unlikely to be useful. """ return self.__schemaStrippedPrefix def setSchemaStrippedPrefix (self, schema_stripped_prefix): @@ -1977,7 +1977,15 @@ def argAddLocationPrefixRewrite (self, prefix_rewrite): Parameter values are strings of the form C{pfx=sub}. The effect is that a schema location that begins with C{pfx} is - rewritten so that it instead begins with C{sub}.""" + rewritten so that it instead begins with C{sub}. + + This applies to schemaLocation attributes in C{import} and + C{include} elements. It may be used to convert absolute + schema locations into relative ones to allow offline + processing when all schema are available in a local + directory. See C{schemaRoot}. + """ + try: (prefix, substituent) = prefix_rewrite.split('=', 1) except: diff --git a/pyxb/bundles/reqif/ReqIF.py b/pyxb/bundles/reqif/ReqIF.py new file mode 100644 index 00000000..c3437485 --- /dev/null +++ b/pyxb/bundles/reqif/ReqIF.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from pyxb.bundles.reqif.raw.ReqIF import * diff --git a/pyxb/bundles/reqif/__init__.py b/pyxb/bundles/reqif/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pyxb/bundles/reqif/_xh11d.py b/pyxb/bundles/reqif/_xh11d.py new file mode 100644 index 00000000..cac78b1b --- /dev/null +++ b/pyxb/bundles/reqif/_xh11d.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from pyxb.bundles.reqif.raw._xh11d import * diff --git a/pyxb/bundles/reqif/driver.py b/pyxb/bundles/reqif/driver.py new file mode 100644 index 00000000..4ebb9c93 --- /dev/null +++ b/pyxb/bundles/reqif/driver.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from pyxb.bundles.reqif.raw.driver import * diff --git a/pyxb/bundles/reqif/examples/process.py b/pyxb/bundles/reqif/examples/process.py new file mode 100644 index 00000000..7182278e --- /dev/null +++ b/pyxb/bundles/reqif/examples/process.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- + +import pyxb.bundles.reqif.driver +import pyxb.bundles.reqif.ReqIF as ReqIF + +xml = open('in.xml').read(); +doc = ReqIF.CreateFromDocument(xml) +print(doc.THE_HEADER.REQ_IF_HEADER.COMMENT) diff --git a/pyxb/bundles/reqif/examples/test.expected b/pyxb/bundles/reqif/examples/test.expected new file mode 100644 index 00000000..c5ff7533 --- /dev/null +++ b/pyxb/bundles/reqif/examples/test.expected @@ -0,0 +1 @@ +Optional comment associated with the Exchange Document as a whole diff --git a/pyxb/bundles/reqif/examples/test.sh b/pyxb/bundles/reqif/examples/test.sh new file mode 100755 index 00000000..c150b399 --- /dev/null +++ b/pyxb/bundles/reqif/examples/test.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +: ${PYXB_TEST_ROOT:=${PYXB_ROOT}/tests} +. ${PYXB_TEST_ROOT}/support.sh + +TEST_URI=https://raw.githubusercontent.com/redsteve/EnterpriseArchitect_ReqIF_AddIn/master/EA_ReqIF_AddIn/example.reqif.xml + +if [ ! -f in.xml ]; then + wget -O in.xml ${TEST_URI} || fail retrieving document +fi + +PYXB_ARCHIVE_PATH=${PYXB_ROOT}/pyxb/bundles/reqif//:+ +export PYXB_ARCHIVE_PATH + +python process.py > test.out || fail running +cmp test.out test.expected || fail output comparison +passed diff --git a/pyxb/bundles/reqif/scripts/genbind b/pyxb/bundles/reqif/scripts/genbind new file mode 100755 index 00000000..5c3f4ffc --- /dev/null +++ b/pyxb/bundles/reqif/scripts/genbind @@ -0,0 +1,47 @@ +# Attempt to prevent catastrophe by validating required settings +# and aborting on any subshell error +set -e +if [ -z "${PYXB_ROOT+notset}" ] ; then + echo 1>&2 ERROR: PYXB_ROOT not set + exit 1 +fi + +BUNDLE_TAG=reqif + +. ${PYXB_ROOT}/maintainer/bundlesupport.sh + +# NOTE: w3 imposes a 20-30 s delay when retrieving schema from their +# site. Be patient. +( mkdir -p ${SCHEMA_DIR} \ + && cd ${SCHEMA_DIR} \ + && ( [ -f reqif.xsd ] || wget http://www.omg.org/spec/ReqIF/20110401/reqif.xsd ) \ + && ( [ -f driver.xsd ] || wget http://www.omg.org/spec/ReqIF/20110402/driver.xsd ) \ + && ( [ -f xhtml-attribs-1.xsd ] || wget http://www.w3.org/TR/xhtml-modularization/SCHEMA/xhtml-attribs-1.xsd ) \ + && ( [ -f xhtml-blkphras-1.xsd ] || wget http://www.w3.org/TR/xhtml-modularization/SCHEMA/xhtml-blkphras-1.xsd ) \ + && ( [ -f xhtml-blkpres-1.xsd ] || wget http://www.w3.org/TR/xhtml-modularization/SCHEMA/xhtml-blkpres-1.xsd ) \ + && ( [ -f xhtml-blkstruct-1.xsd ] || wget http://www.w3.org/TR/xhtml-modularization/SCHEMA/xhtml-blkstruct-1.xsd ) \ + && ( [ -f xhtml-datatypes-1.xsd ] || wget http://www.w3.org/TR/xhtml-modularization/SCHEMA/xhtml-datatypes-1.xsd ) \ + && ( [ -f xhtml-edit-1.xsd ] || wget http://www.w3.org/TR/xhtml-modularization/SCHEMA/xhtml-edit-1.xsd ) \ + && ( [ -f xhtml-framework-1.xsd ] || wget http://www.w3.org/TR/xhtml-modularization/SCHEMA/xhtml-framework-1.xsd ) \ + && ( [ -f xhtml-hypertext-1.xsd ] || wget http://www.w3.org/TR/xhtml-modularization/SCHEMA/xhtml-hypertext-1.xsd ) \ + && ( [ -f xhtml-inlphras-1.xsd ] || wget http://www.w3.org/TR/xhtml-modularization/SCHEMA/xhtml-inlphras-1.xsd ) \ + && ( [ -f xhtml-inlpres-1.xsd ] || wget http://www.w3.org/TR/xhtml-modularization/SCHEMA/xhtml-inlpres-1.xsd ) \ + && ( [ -f xhtml-inlstruct-1.xsd ] || wget http://www.w3.org/TR/xhtml-modularization/SCHEMA/xhtml-inlstruct-1.xsd ) \ + && ( [ -f xhtml-inlstyle-1.xsd ] || wget http://www.w3.org/TR/xhtml-modularization/SCHEMA/xhtml-inlstyle-1.xsd ) \ + && ( [ -f xhtml-list-1.xsd ] || wget http://www.w3.org/TR/xhtml-modularization/SCHEMA/xhtml-list-1.xsd ) \ + && ( [ -f xhtml-object-1.xsd ] || wget http://www.w3.org/TR/xhtml-modularization/SCHEMA/xhtml-object-1.xsd ) \ + && ( [ -f xhtml-param-1.xsd ] || wget http://www.w3.org/TR/xhtml-modularization/SCHEMA/xhtml-param-1.xsd ) \ + && ( [ -f xhtml-pres-1.xsd ] || wget http://www.w3.org/TR/xhtml-modularization/SCHEMA/xhtml-pres-1.xsd ) \ + && ( [ -f xhtml-table-1.xsd ] || wget http://www.w3.org/TR/xhtml-modularization/SCHEMA/xhtml-table-1.xsd ) \ + && ( [ -f xhtml-text-1.xsd ] || wget http://www.w3.org/TR/xhtml-modularization/SCHEMA/xhtml-text-1.xsd ) \ +) || failure unable to obtain schema + +pyxbgen \ + --module-prefix=${MODULE_PREFIX} \ + --write-for-customization \ + --allow-builtin-generation \ + --archive-to-file=${ARCHIVE_DIR}/reqif.wxs \ + --schema-root=${SCHEMA_DIR} \ + --location-prefix-rewrite http://www.w3.org/TR/xhtml-modularization/SCHEMA/=${SCHEMA_DIR}/ \ + -u driver.xsd -m driver \ + -u reqif.xsd -m ReqIF diff --git a/pyxb/namespace/__init__.py b/pyxb/namespace/__init__.py index 28e6a05b..8b3e09e8 100644 --- a/pyxb/namespace/__init__.py +++ b/pyxb/namespace/__init__.py @@ -800,6 +800,8 @@ def setPrefix (self, prefix): if self.__boundPrefix == prefix: return self raise pyxb.NamespaceError(self, 'Cannot change the prefix of a bound namespace') + if (None is not prefix) and (0 == len(prefix)): + raise pyxb.UsageError('prefix must be non-empty string') self.__prefix = prefix return self diff --git a/pyxb/namespace/builtin.py b/pyxb/namespace/builtin.py index 0b4a4a41..92b3c58a 100644 --- a/pyxb/namespace/builtin.py +++ b/pyxb/namespace/builtin.py @@ -252,6 +252,7 @@ def _defineBuiltins_ox (self, structures_module): default_namespace=XMLSchema) """There really isn't a schema for this, but it's used as the default namespace in the XML schema, so define it.""" +XHTML.configureCategories(['typeBinding', 'elementBinding']); # http://www.w3.org/2001/xml.xsd XML = _XML('http://www.w3.org/XML/1998/namespace', diff --git a/pyxb/utils/utility.py b/pyxb/utils/utility.py index 8385fc8f..549f8970 100644 --- a/pyxb/utils/utility.py +++ b/pyxb/utils/utility.py @@ -132,6 +132,10 @@ def _SetXMLIdentifierToPython (xml_identifier_to_python): perform that translation before the invalid characters are stripped. + For example, see `unidecode + `_ and `this forum posting + `_. + It is not the responsibility of this callable to do anything other than replace whatever characters it wishes to. All transformations performed by L{MakeIdentifier} will still be @@ -143,6 +147,7 @@ def _SetXMLIdentifierToPython (xml_identifier_to_python): default implementation, which is L{_DefaultXMLIdentifierToPython}. @rtype: C{unicode} + """ global _XMLIdentifierToPython if xml_identifier_to_python is None: diff --git a/pyxb/utils/xmlre.py b/pyxb/utils/xmlre.py index 913ed0a5..6e9e50e0 100644 --- a/pyxb/utils/xmlre.py +++ b/pyxb/utils/xmlre.py @@ -288,7 +288,7 @@ def XMLToPython (pattern): that matches the same language as C{pattern}.""" assert isinstance(pattern, six.text_type) new_pattern_elts = [] - new_pattern_elts.append('^') + new_pattern_elts.append('^(') position = 0 while position < len(pattern): cg = MaybeMatchCharacterClass(pattern, position) @@ -305,5 +305,5 @@ def XMLToPython (pattern): else: (cps, position) = cg new_pattern_elts.append(cps.asPattern()) - new_pattern_elts.append('$') + new_pattern_elts.append(')$') return ''.join(new_pattern_elts) diff --git a/pyxb/xmlschema/structures.py b/pyxb/xmlschema/structures.py index 4603fad9..4aa26171 100644 --- a/pyxb/xmlschema/structures.py +++ b/pyxb/xmlschema/structures.py @@ -97,6 +97,10 @@ def _clearNamespaceContext (self): # represent them, so need a binding-level name. __nameInBinding = None + # The public unique name by which this component is referenced within the + # binding class (as opposed to the binding module). + __uniqueNameInBinding = None + # The schema component that owns this. If C{None}, the component is owned # directly by the schema. __owner = None @@ -241,6 +245,7 @@ def _resetClone_csc (self, **kw): assert self.__cloneSource is not None owner = kw['owner'] self.__nameInBinding = None + self.__uniqueNameInBinding = None self.__owner = owner assert not (isinstance(self, ComplexTypeDefinition) and isinstance(owner, Schema)) self.__ownedComponents = set() @@ -304,8 +309,7 @@ def bestNCName (self): return None def nameInBinding (self): - """Return the name by which this component is known in the generated - binding. + """Return the name by which this component is known in the binding module. @note: To support builtin datatypes, type definitions with an associated L{pythonSupport} class @@ -314,6 +318,10 @@ def nameInBinding (self): with a language keyword, this should be fine.""" return self.__nameInBinding + def uniqueNameInBinding (self): + """Return the name by which this component is known in the binding class.""" + return self.__uniqueNameInBinding + def hasBinding (self): """Return C{True} iff this is a component which has a user-visible Python construct which serves as its binding. @@ -324,10 +332,15 @@ def hasBinding (self): return self.isTypeDefinition() or (isinstance(self, ElementDeclaration) and self._scopeIsGlobal()) def setNameInBinding (self, name_in_binding): - """Set the name by which this component shall be known in the XSD binding.""" + """Set the name by which this component shall be known in the binding module.""" self.__nameInBinding = name_in_binding return self + def setUniqueNameInBinding (self, unique_name): + """Set the name by which this component shall be known in the binding class.""" + self.__uniqueNameInBinding = unique_name + return self + def _updateFromOther_csc (self, other): """Override fields in this instance with those from the other. @@ -1064,6 +1077,18 @@ def _baseDeclaration (self, referenced_declaration): self.__baseDeclaration = referenced_declaration.baseDeclaration() return self.__baseDeclaration + # Indicates that declaration replaces a (compatible) declaration with the + # same name within its scope. Also provide access to the declaration it + # replaces. + __overridesParentScope = False + def overridesParentScope (self): + return self.__overridesParentScope + def _overrideParentScope (self, value): + self.__overriddenDeclaration = value; + self.__overridesParentScope = True + def overriddenDeclaration (self): + return self.__overriddenDeclaration + class _AttributeWildcard_mixin (pyxb.cscRoot): """Support for components that accept attribute wildcards. @@ -1427,6 +1452,7 @@ def CreateFromDOM (cls, node, **kw): def isResolved (self): return self.__attributeDeclaration is not None + # res:AU res:AttributeUse def _resolve (self): if self.isResolved(): return self @@ -1490,10 +1516,13 @@ def _typeDefinition (self, type_definition): # complex. ct = type_definition.contentType() if ct is None: - if False == self.__typeDefinition._isComplexContent(): - failed = False - else: - _log.error('Unable to check value constraint on %s due to incomplete resolution of type', self.expandedName()) + # Either it's not complex, or we can't tell yet; neither + # are failures, but in the latter case delay assigning the + # type until we know. + failed = False + if False != self.__typeDefinition._isComplexContent(): + self.__typeDefinition = None + self._queueForResolution('unresolved base type'); else: failed = not (isinstance(ct, tuple) and (ComplexTypeDefinition.CT_SIMPLE == ct[0])) if failed: @@ -1726,13 +1755,19 @@ def _resolve (self): raise pyxb.SchemaValidationError('Element declaration refers to unrecognized substitution group %s' % (self.__substitutionGroupExpandedName,)) self.__substitutionGroupAffiliation = sga + resolved = True if self.__typeDefinition is None: assert self.__typeExpandedName is not None td = self.__typeExpandedName.typeDefinition() if td is None: raise pyxb.SchemaValidationError('Type declaration %s cannot be found' % (self.__typeExpandedName,)) + # Type definition may be delayed for default value validation; if + # so, we're not really resolved. self._typeDefinition(td) - self.__isResolved = True + resolved = self.typeDefinition() is not None + + self.__isResolved = resolved + return self def _walkParticleTree (self, visit, arg): @@ -1845,6 +1880,14 @@ def _recordLocalDeclaration (self, decl): pending_type = decl.typeDefinition() if not pending_type.isDerivationConsistent(existing_type): raise pyxb.SchemaValidationError('Conflicting element declarations for %s: existing %s versus new %s' % (decl.expandedName(), existing_type, pending_type)) + # If we're deriving by restriction and the declaration has a + # different type than the one in the parent scope, discard the + # parent scope declaration and replace it with this one rather + # than treating the parent as a base. + if (existing_type != pending_type) and (self.DM_restriction == self.derivationMethod()): + decl._overrideParentScope(existing_decl); + scope_map[decl_en] = decl; + existing_decl = decl elif isinstance(decl, AttributeDeclaration): raise pyxb.SchemaValidationError('Multiple attribute declarations for %s' % (decl.expandedName(),)) else: @@ -1940,7 +1983,7 @@ def _updateFromOther_csc (self, other): if not other.isResolved(): if pyxb.namespace.BuiltInObjectUID != self._objectOrigin().generationUID(): - self.__derivationMethod = None + self.__isResolved = False return self @@ -2005,7 +2048,7 @@ def UrTypeDefinition (cls, schema=None, in_builtin_definition=False): bi.setNameInBinding(bi.name()) # The ur-type is always resolved - bi.__derivationMethod = cls.DM_restriction + bi.__isResolved = True cls.__UrTypeDefinition = bi return cls.__UrTypeDefinition @@ -2200,10 +2243,8 @@ def __completeProcessing (self, method, content_style): del self.__attributeGroups self.__ckw = None - # Only now that we've succeeded do we store the method, which - # marks this component resolved. - - self.__derivationMethod = method + # Mark the type resolved + self.__isResolved = True return self def __simpleContent (self, method, **kw): @@ -2242,7 +2283,6 @@ def __simpleContent (self, method, **kw): __PrivateTransient.update(['ctscRestrictionNode' ]) __effectiveMixed = None __effectiveContent = None - __pendingDerivationMethod = None __isComplexContent = None def _isComplexContent (self): return self.__isComplexContent @@ -2361,6 +2401,7 @@ def __complexContent (self, method): assert (self.CT_EMPTY == content_type) or ((type(content_type) == tuple) and (content_type[1] is not None)) return content_type + __isResolved = False def isResolved (self): """Indicate whether this complex type is fully defined. @@ -2378,13 +2419,13 @@ def isResolved (self): latter in the list of type definitions to be resolved. See Schema._addNamedComponent. """ - # Only unresolved nodes have an unset derivationMethod - return (self.__derivationMethod is not None) + return self.__isResolved # Back door to allow the ur-type to re-resolve itself. Only needed when # we're generating bindings for XMLSchema itself. def _setDerivationMethod (self, derivation_method): self.__derivationMethod = derivation_method + self.__isResolved = True return self def __setContentFromDOM (self, node, **kw): @@ -2449,8 +2490,7 @@ def __setContentFromDOM (self, node, **kw): self.__baseTypeDefinition = None # The content is defined by the restriction/extension element definition_node_list = ions.childNodes - # deriviationMethod is assigned after resolution completes - self.__pendingDerivationMethod = method + self.__derivationMethod = method self.__isComplexContent = is_complex_content self.__ctscRestrictionNode = ctsc_restriction_node self.__ctscClause2STD = clause2_std @@ -2464,7 +2504,7 @@ def __setContentFromDOM (self, node, **kw): self.__anyAttribute = any_attribute if self.__isComplexContent: - self.__setComplexContentFromDOM(node, content_node, definition_node_list, self.__pendingDerivationMethod, **kw) + self.__setComplexContentFromDOM(node, content_node, definition_node_list, self.__derivationMethod, **kw) # Creation does not attempt to do resolution. Queue up the newly created # whatsis so we can resolve it after everything's been read in. @@ -2513,11 +2553,11 @@ def _resolve (self): # depends on the base type which we know is good. if self.__contentType is None: if self.__isComplexContent: - content_type = self.__complexContent(self.__pendingDerivationMethod) + content_type = self.__complexContent(self.__derivationMethod) self.__contentStyle = 'complex' else: # The definition node list is not relevant to simple content - content_type = self.__simpleContent(self.__pendingDerivationMethod) + content_type = self.__simpleContent(self.__derivationMethod) if content_type is None: self._queueForResolution('restriction of unresolved simple type') return self @@ -2537,7 +2577,7 @@ def _resolve (self): return self self.__contentType = (self.__contentType[0], prt._adaptForScope(self, self)) - return self.__completeProcessing(self.__pendingDerivationMethod, self.__contentStyle) + return self.__completeProcessing(self.__derivationMethod, self.__contentStyle) def pythonSupport (self): """Complex type definitions have no built-in type support.""" @@ -2892,7 +2932,15 @@ def CreateFromDOM (cls, node, **kw): continue if Particle.IsParticleNode(cn): # NB: Ancestor of particle is set in the ModelGroup constructor - particles.append(Particle.CreateFromDOM(node=cn, **kw)) + particle = Particle.CreateFromDOM(node=cn, **kw); + if 0 == particle.maxOccurs(): + if getattr(cn, '_location', False): + location = ' at ' + str(cn._location()); + else: + location = ''; + _log.warning('Particle %s%s discarded due to maxOccurs 0' % (particle, location)) + else: + particles.append(particle) elif not xsd.nodeIsNamed(cn, 'annotation'): raise pyxb.SchemaValidationError('Unexpected element %s in model group' % (cn.nodeName,)) rv = cls(compositor, particles, node=node, **kw) diff --git a/setup.py b/setup.py index 4b94027f..80ab9322 100755 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ import sys # The current version of the system. Format is #.#.#[-DEV]. -version = '1.2.5' +version = '1.2.6' # Require Python 2.6 or higher or Python 3.1 or higher if (sys.version_info[:2] < (2, 6)) or ((sys.version_info[0] == 3) and sys.version_info[:2] < (3, 1)): diff --git a/tests/datatypes/test-duration.py b/tests/datatypes/test-duration.py index ff29a248..86cfc8fc 100644 --- a/tests/datatypes/test-duration.py +++ b/tests/datatypes/test-duration.py @@ -39,6 +39,18 @@ def testBasic (self): self.assertEqual(1347, v.durationData()['months']) self.assertEqual('P1347M', v.xsdLiteral()) + v = xsd.duration('PT2000000000S') + self.assertEqual(23148, v.days) + self.assertEqual(12800, v.seconds) + self.assertEqual(0, v.microseconds) + self.assertEqual('PT2000000000S', v.xsdLiteral()) + + v = xsd.duration('PT2000000000.3S') + self.assertEqual(23148, v.days) + self.assertEqual(12800, v.seconds) + self.assertEqual(300000, v.microseconds) + self.assertEqual('PT2000000000.3S', v.xsdLiteral()) + v = xsd.duration('P0Y1347M0D') self.assertEqual(0, v.days) self.assertEqual(0, v.seconds) @@ -108,8 +120,10 @@ def testCreation (self): self.assertEqual(3, v.days) self.assertEqual(14842, v.seconds) self.assertEqual('P3DT4H7M22.5S', v.xsdLiteral()) - self.assertRaises(pyxb.SimpleTypeValueError, xsd.duration) - self.assertRaises(pyxb.SimpleTypeValueError, xsd.duration, 4) + self.assertEqual(datetime.timedelta(), xsd.duration()) + self.assertRaises(pyxb.SimpleTypeValueError, xsd.duration, _from_xml=True) + self.assertEqual(datetime.timedelta(4), xsd.duration(4)) + self.assertRaises(pyxb.SimpleTypeValueError, xsd.duration, 4, _from_xml=True) if __name__ == '__main__': unittest.main() diff --git a/tests/datatypes/test-time.py b/tests/datatypes/test-time.py index 2b21a047..013b0d27 100644 --- a/tests/datatypes/test-time.py +++ b/tests/datatypes/test-time.py @@ -24,6 +24,7 @@ def testBad (self): self.assertRaises(pyxb.SimpleTypeValueError, xsd.time, '12:14:32.Z') self.assertRaises(pyxb.SimpleTypeValueError, xsd.time, '12:14:32.123405:00') self.assertRaises(pyxb.SimpleTypeValueError, xsd.time, '12:14:32.1234+05') + self.assertRaises(pyxb.SimpleTypeValueError, xsd.time, _from_xml=True) def testFromText (self): self.verifyTime(xsd.time('12:14:32'), with_usec=False, with_tzinfo=False) diff --git a/tests/drivers/test-facets.py b/tests/drivers/test-facets.py index 5316dd9c..cda57825 100644 --- a/tests/drivers/test-facets.py +++ b/tests/drivers/test-facets.py @@ -27,5 +27,10 @@ def testQuantity (self): self.assertRaises(Exception, quantity, -52) self.assertRaises(Exception, quantity, 100) + def testEmptyQuantity(self): + xml = '' + dom = pyxb.utils.domutils.StringToDOM(xml).documentElement; + self.assertRaises(SimpleTypeValueError, CreateFromDOM, dom) + if __name__ == '__main__': unittest.main() diff --git a/tests/support.sh b/tests/support.sh new file mode 100644 index 00000000..34fc8868 --- /dev/null +++ b/tests/support.sh @@ -0,0 +1,59 @@ +# POSIX shell infrastructure included by various test scripts to +# remove redundancy and simplify the scripts. +# +# REMOVE_ON_EXIT: A list of files that will be removed when the +# including script exits. +# + +# Untested failures are test failures +set -e + +# Improve comparability by using the same encoding as the developer's +# console environment when redirecting to a file. +export PYTHONIOENCODING='utf-8' + +# Capture the directory from which the test script was run. +TEST_DIR=$(cd $(dirname ${0}); pwd) +echo "TEST: ${TEST_DIR}" + +# Don't throw away temp file names if somebody includes this multiple +# times. +${REMOVE_ON_EXIT:=} + +# Create a temporary file into which output can be directed for +# comparison with expected results. The file name is placed in +# variable tmpout and is also cached for removal when the script +# exits. If you need multiple temporary outputs you may invoke this +# multiple times. +# +# Usage: name=$(make_tmpout [base]) +make_tmpout () { + tmpout=$(mktemp -t ${1:-test}_XXXXXXXXXX) + REMOVE_ON_EXIT="${REMOVE_ON_EXIT} ${tmpout}" + echo ${tmpout} +} + +# Remove remaining temporary files +cleanup () { + rm -f ${REMOVE_ON_EXIT} +} +trap cleanup EXIT + +# Indicate a test failure, with arguments displayed as a message, +# and exit with an error. If tmpout specifies a non-empty file +# its contents will be copied +fail () { + echo 1>&2 "TEST: FAIL: ${TEST_DIR}${@:+: $@}" + exit 1 +} + +# Indicate the test passed, with arguments displayed as a message, +# and continue to execute. +passed () { + echo 1>&2 "TEST: PASS: ${TEST_DIR}${@:+: $@}" +} + +# Local Variables: +# mode:sh +# indent-tabs-mode:nil +# End: diff --git a/tests/trac/test-issue-0066.py b/tests/trac/test-issue-0066.py new file mode 100644 index 00000000..77fe64b7 --- /dev/null +++ b/tests/trac/test-issue-0066.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals +import logging +if __name__ == '__main__': + logging.basicConfig() +_log = logging.getLogger(__name__) +import pyxb.binding.generate +import pyxb.utils.domutils +import pyxb.binding.datatypes as xs +import datetime; +from xml.dom import Node + +import os.path +xsd=''' + + + +''' + +code = pyxb.binding.generate.GeneratePython(schema_text=xsd) +#open('code.py', 'w').write(code) +#print code + +rv = compile(code, 'test', 'exec') +eval(rv) + +from pyxb.exceptions_ import * + +import unittest + +class TestIssue0066 (unittest.TestCase): + def test (self): + instance = elt(4); + xmlt = '4'; + xmld = xmlt.encode('utf-8'); + self.assertEqual(instance.toxml('utf-8', root_only=True), xmld); + self.assertRaises(UsageError, Namespace.setPrefix, ''); + +if __name__ == '__main__': + unittest.main() diff --git a/tests/trac/test-issue-0069.py b/tests/trac/test-issue-0069.py new file mode 100644 index 00000000..d94f21c9 --- /dev/null +++ b/tests/trac/test-issue-0069.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals +import logging +if __name__ == '__main__': + logging.basicConfig() +_log = logging.getLogger(__name__) +import pyxb.binding.generate +import pyxb.utils.domutils +from xml.dom import Node + +import os.path +xsd=''' + + + + + + + + + + + + + + + + +''' + +code = pyxb.binding.generate.GeneratePython(schema_text=xsd) +#open('code.py', 'w').write(code) +#print code + +rv = compile(code, 'test', 'exec') +eval(rv) + +from pyxb.exceptions_ import * + +import unittest + +class TestIssue0069 (unittest.TestCase): + def testCountry (self): + address = Address() + address.City = 'New-York' + with self.assertRaises(SimpleFacetValueError) as cm: + address.Country = 'USA' + address.Country = 'US' + xmlt = '
New-YorkUS
'; + xmld = xmlt.encode('utf-8'); + self.assertEqual(address.toxml('utf8', root_only=True), xmld) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/trac/test-issue-0071.py b/tests/trac/test-issue-0071.py new file mode 100644 index 00000000..658aae88 --- /dev/null +++ b/tests/trac/test-issue-0071.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +import logging +if __name__ == '__main__': + logging.basicConfig() +_log = logging.getLogger(__name__) +import pyxb.binding.generate +import pyxb.utils.domutils +import pyxb.binding.datatypes as xs +import datetime; +from xml.dom import Node + +import os.path +xsd=''' + + + + + + + +''' + +code = pyxb.binding.generate.GeneratePython(schema_text=xsd) +#open('code.py', 'w').write(code) +#print code + +rv = compile(code, 'test', 'exec') +eval(rv) + +from pyxb.exceptions_ import * + +import unittest + +class TestIssue0071 (unittest.TestCase): + def testNonNegativeInteger (self): + self.assertEqual(0, xs.nonNegativeInteger()); + self.assertEqual(0, CreateFromDocument(six.u('0'))); + self.assertRaises(SimpleTypeValueError, CreateFromDocument, six.u('')); + + def testBoolean (self): + self.assertEqual(0, xs.boolean()); + self.assertEqual(0, CreateFromDocument(six.u('0'))); + self.assertRaises(SimpleTypeValueError, CreateFromDocument, six.u('')); + + def testInt (self): + self.assertEqual(0, xs.int()); + self.assertEqual(0, CreateFromDocument(six.u('0'))); + self.assertRaises(SimpleTypeValueError, CreateFromDocument, six.u('')); + + def testTime (self): + self.assertEqual(datetime.time(), xs.time()); + self.assertEqual(datetime.time(), CreateFromDocument(six.u(''))); + self.assertRaises(SimpleTypeValueError, CreateFromDocument, six.u('