From 0ca52c7c80f1d9eb2a9366f0934f0d8c609b9e74 Mon Sep 17 00:00:00 2001 From: Dave Kuhlman <dkuhlman@davekuhlman.org> Date: Fri, 16 Mar 2018 13:54:04 -0700 Subject: [PATCH] v. 2.29.11 Fix for --no-namespace-defs --- MANIFEST.in | 14 + README.rst | 7 + generateDS.html | 6 +- generateDS.py | 12 +- generateDS.txt | 2 +- generateds_gui_notes.html | 6 +- generateds_gui_notes.txt | 2 +- gui/generateds_gui.py | 2 +- librarytemplate_howto.html | 6 +- librarytemplate_howto.txt | 2 +- process_includes.py | 2 +- setup.py | 2 +- tests/no_namespace_defs.xsd | 36 ++ tests/no_namespace_defs1_sub.py | 67 +++ tests/no_namespace_defs1_sup.py | 849 ++++++++++++++++++++++++++++++ tests/test.py | 25 + tutorial/generateds_tutorial.html | 6 +- tutorial/generateds_tutorial.txt | 2 +- tutorial/generateds_tutorial.zip | Bin 48771 -> 48770 bytes 19 files changed, 1025 insertions(+), 23 deletions(-) create mode 100644 tests/no_namespace_defs.xsd create mode 100644 tests/no_namespace_defs1_sub.py create mode 100644 tests/no_namespace_defs1_sup.py diff --git a/MANIFEST.in b/MANIFEST.in index 99fe7ea..6203fff 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -132,6 +132,20 @@ include tests/rem_dup_elems1_sub.py include tests/reference_simpletype.xsd include tests/reference_simpletype1_sup.py include tests/reference_simpletype1_sub.py +include tests/defaults_cases_always.xsd +include tests/defaults_cases_always1_out.xml +include tests/defaults_cases_always1_sub.py +include tests/defaults_cases_always1_sup.py +include tests/defaults_cases_always.xml +include tests/no_namespace_defs.xsd +include tests/no_namespace_defs1_sub.py +include tests/no_namespace_defs1_sup.py +include tests/mixedcontent.xsd +include tests/mixedcontent.xml +include tests/mixedcontent1_out.xml +include tests/mixedcontent1_sub.py +include tests/mixedcontent1_sup.py +include tests/check_results.rb include gui/generateds_gui.py include gui/generateds_gui.glade diff --git a/README.rst b/README.rst index 1e04616..61b2ca8 100644 --- a/README.rst +++ b/README.rst @@ -141,6 +141,13 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Change history -------------- +Version 2.29.11 (03/16/2018) + +- Fix for the --no-namespace-defs command line option. The work on + namespaces in v. 2.29.6 appears to have conflicted with and + deactivated this. Thanks to Olof Kindgren for reporting this. +- Added unit test for --no-namespace-defs. + Version 2.29.10 (03/14/2018) - Fix to resolution of child types -- Formerly, we were adding some diff --git a/generateDS.html b/generateDS.html index da8d74c..f03cee6 100644 --- a/generateDS.html +++ b/generateDS.html @@ -220,7 +220,7 @@ They are used by updateversion.py. --> <col class="field-name" /> <col class="field-body" /> <tbody valign="top"> -<tr class="field"><th class="field-name">revision:</th><td class="field-body">2.29.10</td> +<tr class="field"><th class="field-name">revision:</th><td class="field-body">2.29.11</td> </tr> </tbody> </table> @@ -229,7 +229,7 @@ They are used by updateversion.py. --> <col class="field-name" /> <col class="field-body" /> <tbody valign="top"> -<tr class="field"><th class="field-name">date:</th><td class="field-body">March 14, 2018</td> +<tr class="field"><th class="field-name">date:</th><td class="field-body">March 16, 2018</td> </tr> </tbody> </table> @@ -3386,7 +3386,7 @@ following among others:</p> <div class="footer"> <hr class="footer" /> <a class="reference external" href="generateDS.txt">View document source</a>. -Generated on: 2018-03-14 18:27 UTC. +Generated on: 2018-03-16 20:52 UTC. Generated by <a class="reference external" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference external" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source. </div> diff --git a/generateDS.py b/generateDS.py index 0d183a9..539392a 100755 --- a/generateDS.py +++ b/generateDS.py @@ -232,7 +232,7 @@ logging.disable(logging.INFO) # Do not modify the following VERSION comments. # Used by updateversion.py. ##VERSION## -VERSION = '2.29.10' +VERSION = '2.29.11' ##VERSION## BaseStrTypes = six.string_types @@ -2836,9 +2836,13 @@ def generateExportFn(wrt, prefix, element, namespace, nameSpacesDef): ns_prefix = SchemaNamespaceDict.get(name) if ns_prefix is not None and ns_prefix[0] is not None: namespace = ns_prefix[0] + ':' - ns_def = 'xmlns:{}'.format(ns_prefix[0]) - if ns_def not in nameSpacesDef: - nameSpacesDef += ' {}="{}"'.format(ns_def, ns_prefix[1]) + # Was the --no-namespace-defs command line option used? + if nameSpacesDef: + if ns_prefix is not None and ns_prefix[0] is not None: + namespace = ns_prefix[0] + ':' + ns_def = 'xmlns:{}'.format(ns_prefix[0]) + if ns_def not in nameSpacesDef: + nameSpacesDef += ' {}="{}"'.format(ns_def, ns_prefix[1]) wrt(" def export(self, outfile, level, namespace_='%s', " "name_='%s', namespacedef_='%s', pretty_print=True):\n" % (namespace, encodedname, nameSpacesDef)) diff --git a/generateDS.txt b/generateDS.txt index 7f7d311..e9c12dd 100644 --- a/generateDS.txt +++ b/generateDS.txt @@ -12,7 +12,7 @@ generateDS -- Generate Data Structures from XML Schema .. version -:revision: 2.29.10 +:revision: 2.29.11 .. version diff --git a/generateds_gui_notes.html b/generateds_gui_notes.html index 7432d82..f566ce5 100644 --- a/generateds_gui_notes.html +++ b/generateds_gui_notes.html @@ -220,7 +220,7 @@ They are used by updateversion.py. --> <col class="field-name" /> <col class="field-body" /> <tbody valign="top"> -<tr class="field"><th class="field-name">revision:</th><td class="field-body">2.29.10</td> +<tr class="field"><th class="field-name">revision:</th><td class="field-body">2.29.11</td> </tr> </tbody> </table> @@ -229,7 +229,7 @@ They are used by updateversion.py. --> <col class="field-name" /> <col class="field-body" /> <tbody valign="top"> -<tr class="field"><th class="field-name">date:</th><td class="field-body">March 14, 2018</td> +<tr class="field"><th class="field-name">date:</th><td class="field-body">March 16, 2018</td> </tr> </tbody> </table> @@ -401,7 +401,7 @@ $ mv generateds_gui.mo locale/ru/LC_MESSAGES/ <div class="footer"> <hr class="footer" /> <a class="reference external" href="generateds_gui_notes.txt">View document source</a>. -Generated on: 2018-03-14 18:27 UTC. +Generated on: 2018-03-16 20:52 UTC. Generated by <a class="reference external" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference external" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source. </div> diff --git a/generateds_gui_notes.txt b/generateds_gui_notes.txt index f8efa78..0cd90f1 100644 --- a/generateds_gui_notes.txt +++ b/generateds_gui_notes.txt @@ -12,7 +12,7 @@ GenerateDS GUI Notes .. version -:revision: 2.29.10 +:revision: 2.29.11 .. version diff --git a/gui/generateds_gui.py b/gui/generateds_gui.py index 1a34eb2..9193a24 100644 --- a/gui/generateds_gui.py +++ b/gui/generateds_gui.py @@ -41,7 +41,7 @@ from libgenerateDS.gui import generateds_gui_session # Do not modify the following VERSION comments. # Used by updateversion.py. ##VERSION## -VERSION = '2.29.10' +VERSION = '2.29.11' ##VERSION## diff --git a/librarytemplate_howto.html b/librarytemplate_howto.html index a5724e3..bf62a12 100644 --- a/librarytemplate_howto.html +++ b/librarytemplate_howto.html @@ -217,7 +217,7 @@ dkuhlman (at) davekuhlman (dot) org <col class="field-name" /> <col class="field-body" /> <tbody valign="top"> -<tr class="field"><th class="field-name">revision:</th><td class="field-body">2.29.10</td> +<tr class="field"><th class="field-name">revision:</th><td class="field-body">2.29.11</td> </tr> </tbody> </table> @@ -226,7 +226,7 @@ dkuhlman (at) davekuhlman (dot) org <col class="field-name" /> <col class="field-body" /> <tbody valign="top"> -<tr class="field"><th class="field-name">date:</th><td class="field-body">March 14, 2018</td> +<tr class="field"><th class="field-name">date:</th><td class="field-body">March 16, 2018</td> </tr> </tbody> </table> @@ -380,7 +380,7 @@ this command for your needs. For example, you may need to use <div class="footer"> <hr class="footer" /> <a class="reference external" href="librarytemplate_howto.txt">View document source</a>. -Generated on: 2018-03-14 18:27 UTC. +Generated on: 2018-03-16 20:52 UTC. Generated by <a class="reference external" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference external" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source. </div> diff --git a/librarytemplate_howto.txt b/librarytemplate_howto.txt index 72b3f8b..a726e0e 100644 --- a/librarytemplate_howto.txt +++ b/librarytemplate_howto.txt @@ -8,7 +8,7 @@ How to package a generateDS.py generated library .. version -:revision: 2.29.10 +:revision: 2.29.11 .. version diff --git a/process_includes.py b/process_includes.py index abbc7da..f7ffb58 100755 --- a/process_includes.py +++ b/process_includes.py @@ -40,7 +40,7 @@ except ImportError: # Do not modify the following VERSION comments. # Used by updateversion.py. ##VERSION## -VERSION = '2.29.10' +VERSION = '2.29.11' ##VERSION## CatalogDict = {} diff --git a/setup.py b/setup.py index ef25b06..4e91566 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ setup(name="generateDS", # Do not modify the following VERSION comments. # Used by updateversion.py. ##VERSION## - version="2.29.10", + version="2.29.11", ##VERSION## author="Dave Kuhlman", author_email="dkuhlman@davekuhlman.org", diff --git a/tests/no_namespace_defs.xsd b/tests/no_namespace_defs.xsd new file mode 100644 index 0000000..6d84a4d --- /dev/null +++ b/tests/no_namespace_defs.xsd @@ -0,0 +1,36 @@ +<?xml version="1.0"?> +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> + + <xs:element name="people" type="peopleType"> + <xs:annotation><xs:documentation> + A list of people. + </xs:documentation></xs:annotation> + </xs:element> + <xs:complexType name="peopleType"> + <xs:sequence> + <xs:element name="person" maxOccurs="unbounded" type="personType"/> + <xs:element name="specialperson" maxOccurs="unbounded" type="specialperson"/> + </xs:sequence> + </xs:complexType> + + <xs:element name="person" type="personType"> + <xs:annotation><xs:documentation> + A generic person. This is the base for a number of different + kinds of people. They are each an extension of this base + type of person. + </xs:documentation></xs:annotation> + </xs:element> + + <xs:complexType name="personType" mixed="0"> + <xs:sequence> + <xs:element name="name" type="xs:string"/> + <xs:element name="interest" type="xs:string" maxOccurs="unbounded" /> + <xs:element name="category" type="xs:integer" minOccurs="0" /> + <xs:element name="description" type="xs:string"/> + </xs:sequence> + <xs:attribute name="value" type="xs:ID" /> + <xs:attribute name="id" type="xs:integer" /> + <xs:attribute name="ratio" type="xs:float" /> + </xs:complexType> + +</xs:schema> diff --git a/tests/no_namespace_defs1_sub.py b/tests/no_namespace_defs1_sub.py new file mode 100644 index 0000000..b54fcb2 --- /dev/null +++ b/tests/no_namespace_defs1_sub.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python + +# +# Generated by generateDS.py. +# Python 2.7.14 (default, Sep 23 2017, 22:06:14) [GCC 7.2.0] +# +# Command line options: +# ('--no-dates', '') +# ('--no-versions', '') +# ('--disable-xml', '') +# ('--disable-generatedssuper-lookup', '') +# ('--member-specs', 'list') +# ('-f', '') +# ('-a', 'xsd:') +# ('-o', 'tests/no_namespace_defs2_sup.py') +# ('-s', 'tests/no_namespace_defs2_sub.py') +# ('--super', 'no_namespace_defs2_sup') +# ('--no-warnings', '') +# +# Command line arguments: +# tests/no_namespace_defs.xsd +# +# Command line: +# generateDS.py --no-dates --no-versions --disable-xml --disable-generatedssuper-lookup --member-specs="list" -f -a "xsd:" -o "tests/no_namespace_defs2_sup.py" -s "tests/no_namespace_defs2_sub.py" --super="no_namespace_defs2_sup" --no-warnings tests/no_namespace_defs.xsd +# +# Current working directory (os.getcwd()): +# generateds +# + +import sys +## from lxml import etree as etree_ + +import no_namespace_defs2_sup as supermod + +## def parsexml_(infile, parser=None, **kwargs): +## if parser is None: +## # Use the lxml ElementTree compatible parser so that, e.g., +## # we ignore comments. +## parser = etree_.ETCompatXMLParser() +## doc = etree_.parse(infile, parser=parser, **kwargs) +## return doc + +# +# Globals +# + +ExternalEncoding = 'ascii' + +# +# Data representation classes +# + + +class peopleTypeSub(supermod.peopleType): + def __init__(self, person=None, specialperson=None): + super(peopleTypeSub, self).__init__(person, specialperson, ) +supermod.peopleType.subclass = peopleTypeSub +# end class peopleTypeSub + + +class personTypeSub(supermod.personType): + def __init__(self, value=None, id=None, ratio=None, name=None, interest=None, category=None, description=None): + super(personTypeSub, self).__init__(value, id, ratio, name, interest, category, description, ) +supermod.personType.subclass = personTypeSub +# end class personTypeSub + + diff --git a/tests/no_namespace_defs1_sup.py b/tests/no_namespace_defs1_sup.py new file mode 100644 index 0000000..ca8cbc7 --- /dev/null +++ b/tests/no_namespace_defs1_sup.py @@ -0,0 +1,849 @@ +## #!/usr/bin/env python +# -*- coding: utf-8 -*- + +# +# Generated by generateDS.py. +# Python 2.7.14 (default, Sep 23 2017, 22:06:14) [GCC 7.2.0] +# +# Command line options: +# ('--no-dates', '') +# ('--no-versions', '') +# ('--disable-xml', '') +# ('--disable-generatedssuper-lookup', '') +# ('--member-specs', 'list') +# ('-f', '') +# ('-a', 'xsd:') +# ('-o', 'tests/no_namespace_defs2_sup.py') +# ('-s', 'tests/no_namespace_defs2_sub.py') +# ('--super', 'no_namespace_defs2_sup') +# ('--no-warnings', '') +# +# Command line arguments: +# tests/no_namespace_defs.xsd +# +# Command line: +# generateDS.py --no-dates --no-versions --disable-xml --disable-generatedssuper-lookup --member-specs="list" -f -a "xsd:" -o "tests/no_namespace_defs2_sup.py" -s "tests/no_namespace_defs2_sub.py" --super="no_namespace_defs2_sup" --no-warnings tests/no_namespace_defs.xsd +# +# Current working directory (os.getcwd()): +# generateds +# + +import sys +import re as re_ +import base64 +import datetime as datetime_ +import warnings as warnings_ +## try: +## from lxml import etree as etree_ +## except ImportError: +## from xml.etree import ElementTree as etree_ + + +Validate_simpletypes_ = True +if sys.version_info.major == 2: + BaseStrType_ = basestring +else: + BaseStrType_ = str + + +## def parsexml_(infile, parser=None, **kwargs): +## if parser is None: +## # Use the lxml ElementTree compatible parser so that, e.g., +## # we ignore comments. +## try: +## parser = etree_.ETCompatXMLParser() +## except AttributeError: +## # fallback to xml.etree +## parser = etree_.XMLParser() +## doc = etree_.parse(infile, parser=parser, **kwargs) +## return doc + +## def parsexmlstring_(instring, parser=None, **kwargs): +## if parser is None: +## # Use the lxml ElementTree compatible parser so that, e.g., +## # we ignore comments. +## try: +## parser = etree_.ETCompatXMLParser() +## except AttributeError: +## # fallback to xml.etree +## parser = etree_.XMLParser() +## element = etree_.fromstring(instring, parser=parser, **kwargs) +## return element + +# +# Namespace prefix definition table (and other attributes, too) +# +# The module generatedsnamespaces, if it is importable, must contain +# a dictionary named GeneratedsNamespaceDefs. This Python dictionary +# should map element type names (strings) to XML schema namespace prefix +# definitions. The export method for any class for which there is +# a namespace prefix definition, will export that definition in the +# XML representation of that element. See the export method of +# any generated element type class for a example of the use of this +# table. +# A sample table is: +# +# # File: generatedsnamespaces.py +# +# GenerateDSNamespaceDefs = { +# "ElementtypeA": "http://www.xxx.com/namespaceA", +# "ElementtypeB": "http://www.xxx.com/namespaceB", +# } +# + +try: + from generatedsnamespaces import GenerateDSNamespaceDefs as GenerateDSNamespaceDefs_ +except ImportError: + GenerateDSNamespaceDefs_ = {} + +# +# The root super-class for element type classes +# +# Calls to the methods in these classes are generated by generateDS.py. +# You can replace these methods by re-implementing the following class +# in a module named generatedssuper.py. + + +class GeneratedsSuper(object): + tzoff_pattern = re_.compile(r'(\+|-)((0\d|1[0-3]):[0-5]\d|14:00)$') + class _FixedOffsetTZ(datetime_.tzinfo): + def __init__(self, offset, name): + self.__offset = datetime_.timedelta(minutes=offset) + self.__name = name + def utcoffset(self, dt): + return self.__offset + def tzname(self, dt): + return self.__name + def dst(self, dt): + return None + def gds_format_string(self, input_data, input_name=''): + return input_data + def gds_validate_string(self, input_data, node=None, input_name=''): + if not input_data: + return '' + else: + return input_data + def gds_format_base64(self, input_data, input_name=''): + return base64.b64encode(input_data) + def gds_validate_base64(self, input_data, node=None, input_name=''): + return input_data + def gds_format_integer(self, input_data, input_name=''): + return '%d' % input_data + def gds_validate_integer(self, input_data, node=None, input_name=''): + return input_data + def gds_format_integer_list(self, input_data, input_name=''): + return '%s' % ' '.join(input_data) + def gds_validate_integer_list( + self, input_data, node=None, input_name=''): + values = input_data.split() + for value in values: + try: + int(value) + except (TypeError, ValueError): + raise_parse_error(node, 'Requires sequence of integers') + return values + def gds_format_float(self, input_data, input_name=''): + return ('%.15f' % input_data).rstrip('0') + def gds_validate_float(self, input_data, node=None, input_name=''): + return input_data + def gds_format_float_list(self, input_data, input_name=''): + return '%s' % ' '.join(input_data) + def gds_validate_float_list( + self, input_data, node=None, input_name=''): + values = input_data.split() + for value in values: + try: + float(value) + except (TypeError, ValueError): + raise_parse_error(node, 'Requires sequence of floats') + return values + def gds_format_double(self, input_data, input_name=''): + return '%e' % input_data + def gds_validate_double(self, input_data, node=None, input_name=''): + return input_data + def gds_format_double_list(self, input_data, input_name=''): + return '%s' % ' '.join(input_data) + def gds_validate_double_list( + self, input_data, node=None, input_name=''): + values = input_data.split() + for value in values: + try: + float(value) + except (TypeError, ValueError): + raise_parse_error(node, 'Requires sequence of doubles') + return values + def gds_format_boolean(self, input_data, input_name=''): + return ('%s' % input_data).lower() + def gds_validate_boolean(self, input_data, node=None, input_name=''): + return input_data + def gds_format_boolean_list(self, input_data, input_name=''): + return '%s' % ' '.join(input_data) + def gds_validate_boolean_list( + self, input_data, node=None, input_name=''): + values = input_data.split() + for value in values: + if value not in ('true', '1', 'false', '0', ): + raise_parse_error( + node, + 'Requires sequence of booleans ' + '("true", "1", "false", "0")') + return values + def gds_validate_datetime(self, input_data, node=None, input_name=''): + return input_data + def gds_format_datetime(self, input_data, input_name=''): + if input_data.microsecond == 0: + _svalue = '%04d-%02d-%02dT%02d:%02d:%02d' % ( + input_data.year, + input_data.month, + input_data.day, + input_data.hour, + input_data.minute, + input_data.second, + ) + else: + _svalue = '%04d-%02d-%02dT%02d:%02d:%02d.%s' % ( + input_data.year, + input_data.month, + input_data.day, + input_data.hour, + input_data.minute, + input_data.second, + ('%f' % (float(input_data.microsecond) / 1000000))[2:], + ) + if input_data.tzinfo is not None: + tzoff = input_data.tzinfo.utcoffset(input_data) + if tzoff is not None: + total_seconds = tzoff.seconds + (86400 * tzoff.days) + if total_seconds == 0: + _svalue += 'Z' + else: + if total_seconds < 0: + _svalue += '-' + total_seconds *= -1 + else: + _svalue += '+' + hours = total_seconds // 3600 + minutes = (total_seconds - (hours * 3600)) // 60 + _svalue += '{0:02d}:{1:02d}'.format(hours, minutes) + return _svalue + @classmethod + def gds_parse_datetime(cls, input_data): + tz = None + if input_data[-1] == 'Z': + tz = GeneratedsSuper._FixedOffsetTZ(0, 'UTC') + input_data = input_data[:-1] + else: + results = GeneratedsSuper.tzoff_pattern.search(input_data) + if results is not None: + tzoff_parts = results.group(2).split(':') + tzoff = int(tzoff_parts[0]) * 60 + int(tzoff_parts[1]) + if results.group(1) == '-': + tzoff *= -1 + tz = GeneratedsSuper._FixedOffsetTZ( + tzoff, results.group(0)) + input_data = input_data[:-6] + time_parts = input_data.split('.') + if len(time_parts) > 1: + micro_seconds = int(float('0.' + time_parts[1]) * 1000000) + input_data = '%s.%s' % (time_parts[0], micro_seconds, ) + dt = datetime_.datetime.strptime( + input_data, '%Y-%m-%dT%H:%M:%S.%f') + else: + dt = datetime_.datetime.strptime( + input_data, '%Y-%m-%dT%H:%M:%S') + dt = dt.replace(tzinfo=tz) + return dt + def gds_validate_date(self, input_data, node=None, input_name=''): + return input_data + def gds_format_date(self, input_data, input_name=''): + _svalue = '%04d-%02d-%02d' % ( + input_data.year, + input_data.month, + input_data.day, + ) + try: + if input_data.tzinfo is not None: + tzoff = input_data.tzinfo.utcoffset(input_data) + if tzoff is not None: + total_seconds = tzoff.seconds + (86400 * tzoff.days) + if total_seconds == 0: + _svalue += 'Z' + else: + if total_seconds < 0: + _svalue += '-' + total_seconds *= -1 + else: + _svalue += '+' + hours = total_seconds // 3600 + minutes = (total_seconds - (hours * 3600)) // 60 + _svalue += '{0:02d}:{1:02d}'.format( + hours, minutes) + except AttributeError: + pass + return _svalue + @classmethod + def gds_parse_date(cls, input_data): + tz = None + if input_data[-1] == 'Z': + tz = GeneratedsSuper._FixedOffsetTZ(0, 'UTC') + input_data = input_data[:-1] + else: + results = GeneratedsSuper.tzoff_pattern.search(input_data) + if results is not None: + tzoff_parts = results.group(2).split(':') + tzoff = int(tzoff_parts[0]) * 60 + int(tzoff_parts[1]) + if results.group(1) == '-': + tzoff *= -1 + tz = GeneratedsSuper._FixedOffsetTZ( + tzoff, results.group(0)) + input_data = input_data[:-6] + dt = datetime_.datetime.strptime(input_data, '%Y-%m-%d') + dt = dt.replace(tzinfo=tz) + return dt.date() + def gds_validate_time(self, input_data, node=None, input_name=''): + return input_data + def gds_format_time(self, input_data, input_name=''): + if input_data.microsecond == 0: + _svalue = '%02d:%02d:%02d' % ( + input_data.hour, + input_data.minute, + input_data.second, + ) + else: + _svalue = '%02d:%02d:%02d.%s' % ( + input_data.hour, + input_data.minute, + input_data.second, + ('%f' % (float(input_data.microsecond) / 1000000))[2:], + ) + if input_data.tzinfo is not None: + tzoff = input_data.tzinfo.utcoffset(input_data) + if tzoff is not None: + total_seconds = tzoff.seconds + (86400 * tzoff.days) + if total_seconds == 0: + _svalue += 'Z' + else: + if total_seconds < 0: + _svalue += '-' + total_seconds *= -1 + else: + _svalue += '+' + hours = total_seconds // 3600 + minutes = (total_seconds - (hours * 3600)) // 60 + _svalue += '{0:02d}:{1:02d}'.format(hours, minutes) + return _svalue + def gds_validate_simple_patterns(self, patterns, target): + # pat is a list of lists of strings/patterns. We should: + # - AND the outer elements + # - OR the inner elements + found1 = True + for patterns1 in patterns: + found2 = False + for patterns2 in patterns1: + if re_.search(patterns2, target) is not None: + found2 = True + break + if not found2: + found1 = False + break + return found1 + @classmethod + def gds_parse_time(cls, input_data): + tz = None + if input_data[-1] == 'Z': + tz = GeneratedsSuper._FixedOffsetTZ(0, 'UTC') + input_data = input_data[:-1] + else: + results = GeneratedsSuper.tzoff_pattern.search(input_data) + if results is not None: + tzoff_parts = results.group(2).split(':') + tzoff = int(tzoff_parts[0]) * 60 + int(tzoff_parts[1]) + if results.group(1) == '-': + tzoff *= -1 + tz = GeneratedsSuper._FixedOffsetTZ( + tzoff, results.group(0)) + input_data = input_data[:-6] + if len(input_data.split('.')) > 1: + dt = datetime_.datetime.strptime(input_data, '%H:%M:%S.%f') + else: + dt = datetime_.datetime.strptime(input_data, '%H:%M:%S') + dt = dt.replace(tzinfo=tz) + return dt.time() + def gds_str_lower(self, instring): + return instring.lower() + def get_path_(self, node): + path_list = [] + self.get_path_list_(node, path_list) + path_list.reverse() + path = '/'.join(path_list) + return path + Tag_strip_pattern_ = re_.compile(r'\{.*\}') + def get_path_list_(self, node, path_list): + if node is None: + return + tag = GeneratedsSuper.Tag_strip_pattern_.sub('', node.tag) + if tag: + path_list.append(tag) + self.get_path_list_(node.getparent(), path_list) + def get_class_obj_(self, node, default_class=None): + class_obj1 = default_class + if 'xsi' in node.nsmap: + classname = node.get('{%s}type' % node.nsmap['xsi']) + if classname is not None: + names = classname.split(':') + if len(names) == 2: + classname = names[1] + class_obj2 = globals().get(classname) + if class_obj2 is not None: + class_obj1 = class_obj2 + return class_obj1 + def gds_build_any(self, node, type_name=None): + return None + @classmethod + def gds_reverse_node_mapping(cls, mapping): + return dict(((v, k) for k, v in mapping.iteritems())) + @staticmethod + def gds_encode(instring): + if sys.version_info.major == 2: + return instring.encode(ExternalEncoding) + else: + return instring + @staticmethod + def convert_unicode(instring): + if isinstance(instring, str): + result = quote_xml(instring) + elif sys.version_info.major == 2 and isinstance(instring, unicode): + result = quote_xml(instring).encode('utf8') + else: + result = GeneratedsSuper.gds_encode(str(instring)) + return result + def __eq__(self, other): + if type(self) != type(other): + return False + return self.__dict__ == other.__dict__ + def __ne__(self, other): + return not self.__eq__(other) + +def getSubclassFromModule_(module, class_): + '''Get the subclass of a class from a specific module.''' + name = class_.__name__ + 'Sub' + if hasattr(module, name): + return getattr(module, name) + else: + return None + + +# +# If you have installed IPython you can uncomment and use the following. +# IPython is available from http://ipython.scipy.org/. +# + +## from IPython.Shell import IPShellEmbed +## args = '' +## ipshell = IPShellEmbed(args, +## banner = 'Dropping into IPython', +## exit_msg = 'Leaving Interpreter, back to program.') + +# Then use the following line where and when you want to drop into the +# IPython shell: +# ipshell('<some message> -- Entering ipshell.\nHit Ctrl-D to exit') + +# +# Globals +# + +ExternalEncoding = 'ascii' +Tag_pattern_ = re_.compile(r'({.*})?(.*)') +String_cleanup_pat_ = re_.compile(r"[\n\r\s]+") +Namespace_extract_pat_ = re_.compile(r'{(.*)}(.*)') +CDATA_pattern_ = re_.compile(r"<!\[CDATA\[.*?\]\]>", re_.DOTALL) + +# Change this to redirect the generated superclass module to use a +# specific subclass module. +CurrentSubclassModule_ = None + +# +# Support/utility functions. +# + + +def showIndent(outfile, level, pretty_print=True): + if pretty_print: + for idx in range(level): + outfile.write(' ') + + +def quote_xml(inStr): + "Escape markup chars, but do not modify CDATA sections." + if not inStr: + return '' + s1 = (isinstance(inStr, BaseStrType_) and inStr or '%s' % inStr) + s2 = '' + pos = 0 + matchobjects = CDATA_pattern_.finditer(s1) + for mo in matchobjects: + s3 = s1[pos:mo.start()] + s2 += quote_xml_aux(s3) + s2 += s1[mo.start():mo.end()] + pos = mo.end() + s3 = s1[pos:] + s2 += quote_xml_aux(s3) + return s2 + + +def quote_xml_aux(inStr): + s1 = inStr.replace('&', '&') + s1 = s1.replace('<', '<') + s1 = s1.replace('>', '>') + return s1 + + +def quote_attrib(inStr): + s1 = (isinstance(inStr, BaseStrType_) and inStr or '%s' % inStr) + s1 = s1.replace('&', '&') + s1 = s1.replace('<', '<') + s1 = s1.replace('>', '>') + if '"' in s1: + if "'" in s1: + s1 = '"%s"' % s1.replace('"', """) + else: + s1 = "'%s'" % s1 + else: + s1 = '"%s"' % s1 + return s1 + + +def quote_python(inStr): + s1 = inStr + if s1.find("'") == -1: + if s1.find('\n') == -1: + return "'%s'" % s1 + else: + return "'''%s'''" % s1 + else: + if s1.find('"') != -1: + s1 = s1.replace('"', '\\"') + if s1.find('\n') == -1: + return '"%s"' % s1 + else: + return '"""%s"""' % s1 + + +def get_all_text_(node): + if node.text is not None: + text = node.text + else: + text = '' + for child in node: + if child.tail is not None: + text += child.tail + return text + + +def find_attr_value_(attr_name, node): + attrs = node.attrib + attr_parts = attr_name.split(':') + value = None + if len(attr_parts) == 1: + value = attrs.get(attr_name) + elif len(attr_parts) == 2: + prefix, name = attr_parts + namespace = node.nsmap.get(prefix) + if namespace is not None: + value = attrs.get('{%s}%s' % (namespace, name, )) + return value + + +class GDSParseError(Exception): + pass + + +def raise_parse_error(node, msg): + msg = '%s (element %s/line %d)' % (msg, node.tag, node.sourceline, ) + raise GDSParseError(msg) + + +class MixedContainer: + # Constants for category: + CategoryNone = 0 + CategoryText = 1 + CategorySimple = 2 + CategoryComplex = 3 + # Constants for content_type: + TypeNone = 0 + TypeText = 1 + TypeString = 2 + TypeInteger = 3 + TypeFloat = 4 + TypeDecimal = 5 + TypeDouble = 6 + TypeBoolean = 7 + TypeBase64 = 8 + def __init__(self, category, content_type, name, value): + self.category = category + self.content_type = content_type + self.name = name + self.value = value + def getCategory(self): + return self.category + def getContenttype(self, content_type): + return self.content_type + def getValue(self): + return self.value + def getName(self): + return self.name +## def export(self, outfile, level, name, namespace, +## pretty_print=True): +## if self.category == MixedContainer.CategoryText: +## # Prevent exporting empty content as empty lines. +## if self.value.strip(): +## outfile.write(self.value) +## elif self.category == MixedContainer.CategorySimple: +## self.exportSimple(outfile, level, name) +## else: # category == MixedContainer.CategoryComplex +## self.value.export( +## outfile, level, namespace, name, +## pretty_print=pretty_print) +## def exportSimple(self, outfile, level, name): +## if self.content_type == MixedContainer.TypeString: +## outfile.write('<%s>%s</%s>' % ( +## self.name, self.value, self.name)) +## elif self.content_type == MixedContainer.TypeInteger or \ +## self.content_type == MixedContainer.TypeBoolean: +## outfile.write('<%s>%d</%s>' % ( +## self.name, self.value, self.name)) +## elif self.content_type == MixedContainer.TypeFloat or \ +## self.content_type == MixedContainer.TypeDecimal: +## outfile.write('<%s>%f</%s>' % ( +## self.name, self.value, self.name)) +## elif self.content_type == MixedContainer.TypeDouble: +## outfile.write('<%s>%g</%s>' % ( +## self.name, self.value, self.name)) +## elif self.content_type == MixedContainer.TypeBase64: +## outfile.write('<%s>%s</%s>' % ( +## self.name, +## base64.b64encode(self.value), +## self.name)) +## def to_etree(self, element): +## if self.category == MixedContainer.CategoryText: +## # Prevent exporting empty content as empty lines. +## if self.value.strip(): +## if len(element) > 0: +## if element[-1].tail is None: +## element[-1].tail = self.value +## else: +## element[-1].tail += self.value +## else: +## if element.text is None: +## element.text = self.value +## else: +## element.text += self.value +## elif self.category == MixedContainer.CategorySimple: +## subelement = etree_.SubElement( +## element, '%s' % self.name) +## subelement.text = self.to_etree_simple() +## else: # category == MixedContainer.CategoryComplex +## self.value.to_etree(element) +## def to_etree_simple(self): +## if self.content_type == MixedContainer.TypeString: +## text = self.value +## elif (self.content_type == MixedContainer.TypeInteger or +## self.content_type == MixedContainer.TypeBoolean): +## text = '%d' % self.value +## elif (self.content_type == MixedContainer.TypeFloat or +## self.content_type == MixedContainer.TypeDecimal): +## text = '%f' % self.value +## elif self.content_type == MixedContainer.TypeDouble: +## text = '%g' % self.value +## elif self.content_type == MixedContainer.TypeBase64: +## text = '%s' % base64.b64encode(self.value) +## return text +## def exportLiteral(self, outfile, level, name): +## if self.category == MixedContainer.CategoryText: +## showIndent(outfile, level) +## outfile.write( +## 'model_.MixedContainer(%d, %d, "%s", "%s"),\n' % ( +## self.category, self.content_type, +## self.name, self.value)) +## elif self.category == MixedContainer.CategorySimple: +## showIndent(outfile, level) +## outfile.write( +## 'model_.MixedContainer(%d, %d, "%s", "%s"),\n' % ( +## self.category, self.content_type, +## self.name, self.value)) +## else: # category == MixedContainer.CategoryComplex +## showIndent(outfile, level) +## outfile.write( +## 'model_.MixedContainer(%d, %d, "%s",\n' % ( +## self.category, self.content_type, self.name,)) +## self.value.exportLiteral(outfile, level + 1) +## showIndent(outfile, level) +## outfile.write(')\n') + + +class MemberSpec_(object): + def __init__(self, name='', data_type='', container=0, + optional=0, child_attrs=None, choice=None): + self.name = name + self.data_type = data_type + self.container = container + self.child_attrs = child_attrs + self.choice = choice + self.optional = optional + def set_name(self, name): self.name = name + def get_name(self): return self.name + def set_data_type(self, data_type): self.data_type = data_type + def get_data_type_chain(self): return self.data_type + def get_data_type(self): + if isinstance(self.data_type, list): + if len(self.data_type) > 0: + return self.data_type[-1] + else: + return 'xs:string' + else: + return self.data_type + def set_container(self, container): self.container = container + def get_container(self): return self.container + def set_child_attrs(self, child_attrs): self.child_attrs = child_attrs + def get_child_attrs(self): return self.child_attrs + def set_choice(self, choice): self.choice = choice + def get_choice(self): return self.choice + def set_optional(self, optional): self.optional = optional + def get_optional(self): return self.optional + + +def _cast(typ, value): + if typ is None or value is None: + return value + return typ(value) + +# +# Data representation classes. +# + + +class peopleType(GeneratedsSuper): + member_data_items_ = [ + MemberSpec_('person', 'personType', 1, 0, {u'maxOccurs': u'unbounded', u'type': u'personType', u'name': u'person'}, None), + MemberSpec_('specialperson', 'xs:string', 1, 0, {u'maxOccurs': u'unbounded', u'type': u'xs:string', u'name': u'specialperson'}, None), + ] + subclass = None + superclass = None + def __init__(self, person=None, specialperson=None): + self.original_tagname_ = None + if person is None: + self.person = [] + else: + self.person = person + if specialperson is None: + self.specialperson = [] + else: + self.specialperson = specialperson + def factory(*args_, **kwargs_): + if CurrentSubclassModule_ is not None: + subclass = getSubclassFromModule_( + CurrentSubclassModule_, peopleType) + if subclass is not None: + return subclass(*args_, **kwargs_) + if peopleType.subclass: + return peopleType.subclass(*args_, **kwargs_) + else: + return peopleType(*args_, **kwargs_) + factory = staticmethod(factory) + def get_person(self): return self.person + def set_person(self, person): self.person = person + def add_person(self, value): self.person.append(value) + def insert_person_at(self, index, value): self.person.insert(index, value) + def replace_person_at(self, index, value): self.person[index] = value + def get_specialperson(self): return self.specialperson + def set_specialperson(self, specialperson): self.specialperson = specialperson + def add_specialperson(self, value): self.specialperson.append(value) + def insert_specialperson_at(self, index, value): self.specialperson.insert(index, value) + def replace_specialperson_at(self, index, value): self.specialperson[index] = value + def hasContent_(self): + if ( + self.person or + self.specialperson + ): + return True + else: + return False +# end class peopleType + + +class personType(GeneratedsSuper): + member_data_items_ = [ + MemberSpec_('value', 'xs:string', 0, 1, {'use': 'optional'}), + MemberSpec_('id', 'xs:integer', 0, 1, {'use': 'optional'}), + MemberSpec_('ratio', 'xs:float', 0, 1, {'use': 'optional'}), + MemberSpec_('name', 'xs:string', 0, 0, {u'type': u'xs:string', u'name': u'name'}, None), + MemberSpec_('interest', 'xs:string', 1, 0, {u'maxOccurs': u'unbounded', u'type': u'xs:string', u'name': u'interest'}, None), + MemberSpec_('category', 'xs:integer', 0, 1, {u'type': u'xs:integer', u'name': u'category', u'minOccurs': u'0'}, None), + MemberSpec_('description', 'xs:string', 0, 0, {u'type': u'xs:string', u'name': u'description'}, None), + ] + subclass = None + superclass = None + def __init__(self, value=None, id=None, ratio=None, name=None, interest=None, category=None, description=None): + self.original_tagname_ = None + self.value = _cast(None, value) + self.id = _cast(int, id) + self.ratio = _cast(float, ratio) + self.name = name + if interest is None: + self.interest = [] + else: + self.interest = interest + self.category = category + self.description = description + def factory(*args_, **kwargs_): + if CurrentSubclassModule_ is not None: + subclass = getSubclassFromModule_( + CurrentSubclassModule_, personType) + if subclass is not None: + return subclass(*args_, **kwargs_) + if personType.subclass: + return personType.subclass(*args_, **kwargs_) + else: + return personType(*args_, **kwargs_) + factory = staticmethod(factory) + def get_name(self): return self.name + def set_name(self, name): self.name = name + def get_interest(self): return self.interest + def set_interest(self, interest): self.interest = interest + def add_interest(self, value): self.interest.append(value) + def insert_interest_at(self, index, value): self.interest.insert(index, value) + def replace_interest_at(self, index, value): self.interest[index] = value + def get_category(self): return self.category + def set_category(self, category): self.category = category + def get_description(self): return self.description + def set_description(self, description): self.description = description + def get_value(self): return self.value + def set_value(self, value): self.value = value + def get_id(self): return self.id + def set_id(self, id): self.id = id + def get_ratio(self): return self.ratio + def set_ratio(self, ratio): self.ratio = ratio + def hasContent_(self): + if ( + self.name is not None or + self.interest or + self.category is not None or + self.description is not None + ): + return True + else: + return False +# end class personType + + +GDSClassesMapping = { + 'people': peopleType, + 'person': personType, +} + + + +__all__ = [ + "peopleType", + "personType" +] diff --git a/tests/test.py b/tests/test.py index 56a70b9..ffa9bf7 100755 --- a/tests/test.py +++ b/tests/test.py @@ -899,6 +899,31 @@ class GenTest(unittest.TestCase): self.remove('{}2_sub.py'.format(t_)) self.remove('{}2_out.xml'.format(t_)) + # + # Test for command line option --no-namespace-defs. + def test_036_no_namespace_defs(self): + cmdTempl = ( + 'python generateDS.py --no-dates --no-versions ' + '--disable-xml --disable-generatedssuper-lookup ' + '--member-specs=list -f -a "xsd:" ' + '-o tests/%s2_sup.py -s tests/%s2_sub.py ' + '--super=%s2_sup --no-warnings ' + 'tests/%s.xsd' + ) + t_ = 'no_namespace_defs' + cmd = cmdTempl % (t_, t_, t_, t_, ) + self.executeClean(cmd, cwd='..') + self.compareFiles( + '{}1_sup.py'.format(t_), + '{}2_sup.py'.format(t_), + ('sys.stdout.write',)) + self.compareFiles('{}1_sub.py'.format(t_), '{}2_sub.py'.format(t_)) + # Need to preserve generated files for next command, cleanup at end + # cleanup generated files + #self.remove('{}2_sup.py'.format(t_)) + #self.remove('{}2_sub.py'.format(t_)) + #self.remove('{}2_out.xml'.format(t_)) + def compareFiles(self, left, right, ignore=None): with open(left) as left_file: with open(right) as right_file: diff --git a/tutorial/generateds_tutorial.html b/tutorial/generateds_tutorial.html index 4b83f30..c8c30d8 100644 --- a/tutorial/generateds_tutorial.html +++ b/tutorial/generateds_tutorial.html @@ -219,7 +219,7 @@ They are used by updateversion.py. --> <col class="field-name" /> <col class="field-body" /> <tbody valign="top"> -<tr class="field"><th class="field-name">revision:</th><td class="field-body">2.29.10</td> +<tr class="field"><th class="field-name">revision:</th><td class="field-body">2.29.11</td> </tr> </tbody> </table> @@ -228,7 +228,7 @@ They are used by updateversion.py. --> <col class="field-name" /> <col class="field-body" /> <tbody valign="top"> -<tr class="field"><th class="field-name">date:</th><td class="field-body">March 14, 2018</td> +<tr class="field"><th class="field-name">date:</th><td class="field-body">March 16, 2018</td> </tr> </tbody> </table> @@ -1210,7 +1210,7 @@ named <tt class="docutils literal">garden_api.py</tt>, you can create an instanc <div class="footer"> <hr class="footer" /> <a class="reference external" href="generateds_tutorial.txt">View document source</a>. -Generated on: 2018-03-14 18:27 UTC. +Generated on: 2018-03-16 20:52 UTC. Generated by <a class="reference external" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference external" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source. </div> diff --git a/tutorial/generateds_tutorial.txt b/tutorial/generateds_tutorial.txt index e89fa7e..4218043 100644 --- a/tutorial/generateds_tutorial.txt +++ b/tutorial/generateds_tutorial.txt @@ -11,7 +11,7 @@ generateDS -- Introduction and Tutorial .. version -:revision: 2.29.10 +:revision: 2.29.11 .. version diff --git a/tutorial/generateds_tutorial.zip b/tutorial/generateds_tutorial.zip index febd5dad5775f685cfc1d863691555447c06084d..e7990575fca2070f20ed4e6f7e2ac5703cd65229 100644 GIT binary patch delta 9878 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 Ixc~qF0Kxa8egFUf delta 9881 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 LEe2+}000006Vb#} -- GitLab