xmlgenerator 0.3.0__py3-none-any.whl → 0.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- xmlgenerator/arguments.py +2 -1
- xmlgenerator/bootstrap.py +1 -1
- xmlgenerator/generator.py +368 -228
- xmlgenerator/randomization.py +13 -5
- xmlgenerator/substitution.py +2 -1
- {xmlgenerator-0.3.0.dist-info → xmlgenerator-0.5.0.dist-info}/METADATA +4 -1
- xmlgenerator-0.5.0.dist-info/RECORD +14 -0
- xmlgenerator-0.3.0.dist-info/RECORD +0 -14
- {xmlgenerator-0.3.0.dist-info → xmlgenerator-0.5.0.dist-info}/WHEEL +0 -0
- {xmlgenerator-0.3.0.dist-info → xmlgenerator-0.5.0.dist-info}/entry_points.txt +0 -0
- {xmlgenerator-0.3.0.dist-info → xmlgenerator-0.5.0.dist-info}/licenses/LICENSE +0 -0
- {xmlgenerator-0.3.0.dist-info → xmlgenerator-0.5.0.dist-info}/top_level.txt +0 -0
xmlgenerator/arguments.py
CHANGED
@@ -33,7 +33,7 @@ def _get_parser():
|
|
33
33
|
dest="source_paths",
|
34
34
|
help="paths to xsd schema(s) or directory with xsd schemas"
|
35
35
|
)
|
36
|
-
parser.add_argument(
|
36
|
+
config_arg = parser.add_argument(
|
37
37
|
"-c", "--config",
|
38
38
|
metavar="<config.yml>",
|
39
39
|
dest="config_yaml",
|
@@ -94,6 +94,7 @@ def _get_parser():
|
|
94
94
|
)
|
95
95
|
|
96
96
|
# add shell completions
|
97
|
+
config_arg.complete = shtab.FILE
|
97
98
|
source_arg.complete = shtab.FILE
|
98
99
|
output_arg.complete = shtab.FILE
|
99
100
|
shtab.add_argument_to(parser, ["-C", "--completion"], "print shell completion script (bash, zsh, tcsh)")
|
xmlgenerator/bootstrap.py
CHANGED
@@ -12,7 +12,6 @@ from xmlgenerator.randomization import Randomizer
|
|
12
12
|
from xmlgenerator.substitution import Substitutor
|
13
13
|
from xmlgenerator.validation import XmlValidator
|
14
14
|
|
15
|
-
# TODO конфигурация ограничений - occurs
|
16
15
|
# TODO Generator - обработка стандартных xsd типов
|
17
16
|
# TODO кастомные переменные для локального контекста
|
18
17
|
# TODO валидация по Schematron
|
@@ -90,6 +89,7 @@ def _main():
|
|
90
89
|
|
91
90
|
|
92
91
|
def _setup_loggers(args):
|
92
|
+
logging.addLevelName(logging.WARNING, 'WARN')
|
93
93
|
log_level = logging.DEBUG if args.debug else logging.INFO
|
94
94
|
logger.setLevel(log_level)
|
95
95
|
configuration.logger.setLevel(log_level)
|
xmlgenerator/generator.py
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
import logging
|
2
|
+
from dataclasses import dataclass, replace
|
3
|
+
from decimal import Decimal
|
4
|
+
from typing import Optional, Any, Callable, Dict
|
2
5
|
|
3
|
-
import xmlschema
|
4
6
|
from lxml import etree
|
5
7
|
from xmlschema.validators import XsdComplexType, XsdAtomicRestriction, XsdTotalDigitsFacet, XsdElement, \
|
6
8
|
XsdGroup, XsdFractionDigitsFacet, XsdLengthFacet, XsdMaxLengthFacet, XsdMinExclusiveFacet, XsdMinInclusiveFacet, \
|
@@ -13,12 +15,78 @@ from xmlgenerator.substitution import Substitutor
|
|
13
15
|
logger = logging.getLogger(__name__)
|
14
16
|
|
15
17
|
|
18
|
+
@dataclass
|
19
|
+
class TypeConstraints:
|
20
|
+
min_length: Optional[int] = None
|
21
|
+
max_length: Optional[int] = None
|
22
|
+
min_value: Optional[Any] = None
|
23
|
+
max_value: Optional[Any] = None
|
24
|
+
total_digits: Optional[int] = None
|
25
|
+
fraction_digits: Optional[int] = None
|
26
|
+
patterns: Optional[list] = None
|
27
|
+
rand_config: Optional[Any] = None
|
28
|
+
|
29
|
+
|
16
30
|
class XmlGenerator:
|
17
31
|
def __init__(self, randomizer: Randomizer, substitutor: Substitutor):
|
18
32
|
self.randomizer = randomizer
|
19
33
|
self.substitutor = substitutor
|
20
|
-
|
21
|
-
|
34
|
+
self.generators: Dict[str, Callable[[TypeConstraints], str]] = {
|
35
|
+
# primitive
|
36
|
+
'boolean': self._generate_boolean,
|
37
|
+
'string': self._generate_string,
|
38
|
+
'decimal': self._generate_decimal,
|
39
|
+
'float': self._generate_float,
|
40
|
+
'double': self._generate_double,
|
41
|
+
'duration': self._generate_duration,
|
42
|
+
'dateTime': self._generate_datetime,
|
43
|
+
'date': self._generate_date,
|
44
|
+
'time': self._generate_time,
|
45
|
+
'gYearMonth': self._generate_gyearmonth,
|
46
|
+
'gYear': self._generate_gyear,
|
47
|
+
'gMonthDay': self._generate_gmonthday,
|
48
|
+
'gDay': self._generate_gday,
|
49
|
+
'gMonth': self._generate_gmonth,
|
50
|
+
'hexBinary': self._generate_hex_binary,
|
51
|
+
'base64Binary': self._generate_base64_binary,
|
52
|
+
'anyURI': self._generate_any_uri,
|
53
|
+
'QName': self._generate_qname,
|
54
|
+
'NOTATION': self._generate_notation,
|
55
|
+
|
56
|
+
# derived - from decimal
|
57
|
+
'byte': self._generate_byte,
|
58
|
+
'short': self._generate_short,
|
59
|
+
'int': self._generate_int,
|
60
|
+
'integer': self._generate_integer,
|
61
|
+
'long': self._generate_long,
|
62
|
+
|
63
|
+
'unsignedByte': self._generate_unsigned_byte,
|
64
|
+
'unsignedShort': self._generate_unsigned_short,
|
65
|
+
'unsignedInt': self._generate_unsigned_int,
|
66
|
+
'unsignedLong': self._generate_unsigned_long,
|
67
|
+
|
68
|
+
'positiveInteger': self._generate_positive_integer,
|
69
|
+
'negativeInteger': self._generate_negative_integer,
|
70
|
+
'nonPositiveInteger': self._generate_non_positive_integer,
|
71
|
+
'nonNegativeInteger': self._generate_non_negative_integer,
|
72
|
+
|
73
|
+
# derived - from string
|
74
|
+
'language': lambda c: (_ for _ in ()).throw(Exception('not yet implemented')),
|
75
|
+
'Name': lambda c: (_ for _ in ()).throw(Exception('not yet implemented')),
|
76
|
+
'NCName': lambda c: (_ for _ in ()).throw(Exception('not yet implemented')),
|
77
|
+
'normalizedString': lambda c: (_ for _ in ()).throw(Exception('not yet implemented')),
|
78
|
+
'token': lambda c: (_ for _ in ()).throw(Exception('not yet implemented')),
|
79
|
+
'ID': lambda c: (_ for _ in ()).throw(Exception('not yet implemented')),
|
80
|
+
'IDREF': lambda c: (_ for _ in ()).throw(Exception('not yet implemented')),
|
81
|
+
'IDREFS': lambda c: (_ for _ in ()).throw(Exception('not yet implemented')),
|
82
|
+
'ENTITY': lambda c: (_ for _ in ()).throw(Exception('not yet implemented')),
|
83
|
+
'ENTITIES': lambda c: (_ for _ in ()).throw(Exception('not yet implemented')),
|
84
|
+
'NMTOKEN': lambda c: (_ for _ in ()).throw(Exception('not yet implemented')),
|
85
|
+
'NMTOKENS': lambda c: (_ for _ in ()).throw(Exception('not yet implemented')),
|
86
|
+
}
|
87
|
+
|
88
|
+
def generate_xml(self, xsd_schema, local_config: GeneratorConfig) -> etree.Element:
|
89
|
+
logger.debug('generate xml document...')
|
22
90
|
ns_map = {None if k == '' else k: v for k, v in xsd_schema.namespaces.items() if v != ''}
|
23
91
|
xsd_root_element = xsd_schema.root_elements[0]
|
24
92
|
xml_root_element = etree.Element(xsd_root_element.name, nsmap=ns_map)
|
@@ -26,7 +94,11 @@ class XmlGenerator:
|
|
26
94
|
self._add_elements(xml_tree, xml_root_element, xsd_root_element, local_config)
|
27
95
|
return xml_root_element
|
28
96
|
|
29
|
-
def _add_elements(self, xml_tree, xml_element
|
97
|
+
def _add_elements(self, xml_tree, xml_element, xsd_element, local_config: GeneratorConfig) -> None:
|
98
|
+
rand_config = local_config.randomization
|
99
|
+
min_occurs_conf = rand_config.min_occurs
|
100
|
+
max_occurs_conf = rand_config.max_occurs
|
101
|
+
|
30
102
|
# Process child elements --------------------------------------------------------------------------------------
|
31
103
|
if isinstance(xsd_element, XsdElement):
|
32
104
|
element_xpath = xml_tree.getpath(xml_element)
|
@@ -38,14 +110,14 @@ class XmlGenerator:
|
|
38
110
|
attributes = getattr(xsd_element, 'attributes', dict())
|
39
111
|
if len(attributes) > 0 and xsd_element_type.local_name != 'anyType':
|
40
112
|
for attr_name, attr in attributes.items():
|
41
|
-
logger.debug('element: %s; attribute "%s" [processing]', element_xpath, attr_name)
|
113
|
+
logger.debug('element: %s; attribute: "%s" - [processing]', element_xpath, attr_name)
|
42
114
|
use = attr.use # optional | required | prohibited
|
43
115
|
if use == 'prohibited':
|
44
|
-
logger.debug('element: %s; attribute: "%s" [skipped]', element_xpath, attr_name)
|
116
|
+
logger.debug('element: %s; attribute: "%s" - [skipped]', element_xpath, attr_name)
|
45
117
|
continue
|
46
118
|
elif use == 'optional':
|
47
|
-
if self.randomizer.random() >
|
48
|
-
logger.debug('element: %s; attribute: "%s" [skipped]', element_xpath, attr_name)
|
119
|
+
if self.randomizer.random() > rand_config.probability:
|
120
|
+
logger.debug('element: %s; attribute: "%s" - [skipped]', element_xpath, attr_name)
|
49
121
|
continue
|
50
122
|
|
51
123
|
attr_value = self._generate_value(attr.type, attr_name, local_config)
|
@@ -57,58 +129,82 @@ class XmlGenerator:
|
|
57
129
|
text = self._generate_value(xsd_element_type, xsd_element.name, local_config)
|
58
130
|
xml_element.text = text
|
59
131
|
logger.debug('element: %s = "%s"', element_xpath, text)
|
60
|
-
|
132
|
+
|
61
133
|
elif isinstance(xsd_element_type, XsdAtomicRestriction):
|
62
134
|
text = self._generate_value(xsd_element_type, xsd_element.name, local_config)
|
63
135
|
xml_element.text = text
|
64
136
|
logger.debug('element: %s = "%s"', element_xpath, text)
|
65
|
-
|
137
|
+
|
66
138
|
elif isinstance(xsd_element_type, XsdComplexType):
|
67
139
|
xsd_element_type_content = xsd_element_type.content
|
68
140
|
if isinstance(xsd_element_type_content, XsdGroup):
|
69
141
|
self._add_elements(xml_tree, xml_element, xsd_element_type_content, local_config)
|
70
142
|
else:
|
71
143
|
raise RuntimeError()
|
144
|
+
|
72
145
|
else:
|
73
146
|
raise RuntimeError()
|
74
147
|
|
75
148
|
elif isinstance(xsd_element, XsdGroup):
|
76
149
|
model = xsd_element.model
|
77
150
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
151
|
+
min_occurs = getattr(xsd_element, 'min_occurs', None)
|
152
|
+
max_occurs = getattr(xsd_element, 'max_occurs', None)
|
153
|
+
min_occurs, max_occurs = merge_constraints(
|
154
|
+
schema_min=min_occurs,
|
155
|
+
schema_max=max_occurs,
|
156
|
+
config_min=min_occurs_conf,
|
157
|
+
config_max=max_occurs_conf
|
158
|
+
)
|
159
|
+
if max_occurs is None:
|
160
|
+
max_occurs = 10
|
161
|
+
group_occurs = self.randomizer.integer(min_occurs, max_occurs)
|
162
|
+
logger.debug('add %s (random between %s and %s) groups of type "%s"',
|
163
|
+
group_occurs, min_occurs, max_occurs, model)
|
83
164
|
|
84
165
|
if model == 'all':
|
85
166
|
for _ in range(group_occurs):
|
86
167
|
xsd_group_content = xsd_element.content
|
87
168
|
for xsd_child_element_type in xsd_group_content:
|
88
169
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
170
|
+
min_occurs = getattr(xsd_child_element_type, 'min_occurs', None)
|
171
|
+
max_occurs = getattr(xsd_child_element_type, 'max_occurs', None)
|
172
|
+
min_occurs, max_occurs = merge_constraints(
|
173
|
+
schema_min=min_occurs,
|
174
|
+
schema_max=max_occurs,
|
175
|
+
config_min=min_occurs_conf,
|
176
|
+
config_max=max_occurs_conf
|
177
|
+
)
|
178
|
+
if max_occurs is None:
|
179
|
+
max_occurs = 10
|
180
|
+
element_occurs = self.randomizer.integer(min_occurs, max_occurs)
|
181
|
+
logger.debug('element_occurs: %s (random between %s and %s)', element_occurs, min_occurs,
|
182
|
+
max_occurs)
|
94
183
|
|
95
184
|
for _ in range(element_occurs):
|
96
185
|
xml_child_element = etree.SubElement(xml_element, xsd_child_element_type.name)
|
97
186
|
self._add_elements(xml_tree, xml_child_element, xsd_child_element_type, local_config)
|
98
|
-
return
|
99
187
|
|
100
188
|
elif model == 'sequence':
|
101
189
|
for _ in range(group_occurs):
|
102
190
|
xsd_group_content = xsd_element.content
|
103
191
|
for xsd_child_element_type in xsd_group_content:
|
192
|
+
if isinstance(xsd_child_element_type, XsdElement):
|
104
193
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
194
|
+
min_occurs = getattr(xsd_child_element_type, 'min_occurs', None)
|
195
|
+
max_occurs = getattr(xsd_child_element_type, 'max_occurs', None)
|
196
|
+
min_occurs, max_occurs = merge_constraints(
|
197
|
+
schema_min=min_occurs,
|
198
|
+
schema_max=max_occurs,
|
199
|
+
config_min=min_occurs_conf,
|
200
|
+
config_max=max_occurs_conf
|
201
|
+
)
|
202
|
+
if max_occurs is None:
|
203
|
+
max_occurs = 10
|
204
|
+
element_occurs = self.randomizer.integer(min_occurs, max_occurs)
|
205
|
+
logger.debug('element_occurs: %s (random between %s and %s)', element_occurs, min_occurs,
|
206
|
+
max_occurs)
|
110
207
|
|
111
|
-
if isinstance(xsd_child_element_type, XsdElement):
|
112
208
|
for _ in range(element_occurs):
|
113
209
|
xml_child_element = etree.SubElement(xml_element, xsd_child_element_type.name)
|
114
210
|
self._add_elements(xml_tree, xml_child_element, xsd_child_element_type, local_config)
|
@@ -123,22 +219,28 @@ class XmlGenerator:
|
|
123
219
|
|
124
220
|
else:
|
125
221
|
raise RuntimeError(xsd_child_element_type)
|
126
|
-
return
|
127
222
|
|
128
223
|
elif model == 'choice':
|
129
224
|
for _ in range(group_occurs):
|
130
225
|
xsd_child_element_type = self.randomizer.any(xsd_element)
|
131
226
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
227
|
+
min_occurs = getattr(xsd_child_element_type, 'min_occurs', None)
|
228
|
+
max_occurs = getattr(xsd_child_element_type, 'max_occurs', None)
|
229
|
+
min_occurs, max_occurs = merge_constraints(
|
230
|
+
schema_min=min_occurs,
|
231
|
+
schema_max=max_occurs,
|
232
|
+
config_min=min_occurs_conf,
|
233
|
+
config_max=max_occurs_conf
|
234
|
+
)
|
235
|
+
if max_occurs is None:
|
236
|
+
max_occurs = 10
|
237
|
+
element_occurs = self.randomizer.integer(min_occurs, max_occurs)
|
238
|
+
logger.debug('element_occurs: %s (random between %s and %s)', element_occurs, min_occurs,
|
239
|
+
max_occurs)
|
137
240
|
|
138
241
|
for _ in range(element_occurs):
|
139
242
|
xml_child_element = etree.SubElement(xml_element, xsd_child_element_type.name)
|
140
243
|
self._add_elements(xml_tree, xml_child_element, xsd_child_element_type, local_config)
|
141
|
-
return
|
142
244
|
|
143
245
|
else:
|
144
246
|
raise RuntimeError()
|
@@ -176,71 +278,14 @@ class XmlGenerator:
|
|
176
278
|
# -------------------------------------------------------------------------------------------------------------
|
177
279
|
# Генерируем значения для стандартных типов и типов с ограничениями
|
178
280
|
if isinstance(xsd_type, XsdAtomicBuiltin) or isinstance(xsd_type, XsdAtomicRestriction):
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
total_digits = None
|
187
|
-
fraction_digits = None
|
188
|
-
patterns = getattr(xsd_type, 'patterns', None)
|
189
|
-
|
190
|
-
validators = getattr(xsd_type, 'validators', None)
|
191
|
-
for validator in validators:
|
192
|
-
if isinstance(validator, XsdMinExclusiveFacet):
|
193
|
-
min_value = validator.value
|
194
|
-
elif isinstance(validator, XsdMinInclusiveFacet):
|
195
|
-
min_value = validator.value
|
196
|
-
elif isinstance(validator, XsdMaxExclusiveFacet):
|
197
|
-
max_value = validator.value
|
198
|
-
elif isinstance(validator, XsdMaxInclusiveFacet):
|
199
|
-
max_value = validator.value
|
200
|
-
elif isinstance(validator, XsdLengthFacet):
|
201
|
-
min_length = validator.value
|
202
|
-
max_length = validator.value
|
203
|
-
elif isinstance(validator, XsdMinLengthFacet):
|
204
|
-
min_length = validator.value
|
205
|
-
elif isinstance(validator, XsdMaxLengthFacet):
|
206
|
-
max_length = validator.value
|
207
|
-
elif isinstance(validator, XsdTotalDigitsFacet):
|
208
|
-
total_digits = validator.value
|
209
|
-
elif isinstance(validator, XsdFractionDigitsFacet):
|
210
|
-
fraction_digits = validator.value
|
211
|
-
elif isinstance(validator, XsdEnumerationFacets):
|
212
|
-
pass
|
213
|
-
elif callable(validator):
|
214
|
-
pass
|
215
|
-
else:
|
216
|
-
raise RuntimeError(f"Unhandled validator: {validator}")
|
281
|
+
constraints = extract_type_constraints(xsd_type, local_config)
|
282
|
+
type_id = xsd_type.id or xsd_type.base_type.id or xsd_type.root_type.id
|
283
|
+
logger.debug('generate value for type: "%s"', type_id)
|
284
|
+
generator = self.generators.get(type_id)
|
285
|
+
if generator is None:
|
286
|
+
raise RuntimeError(f"Generator not found for type: {type_id}")
|
287
|
+
generated_value = generator(constraints)
|
217
288
|
|
218
|
-
rand_config = local_config.randomization
|
219
|
-
|
220
|
-
logger.debug(
|
221
|
-
'restrictions before override: min_length: %4s; max_length: %4s; min_value: %4s; max_value: %4s',
|
222
|
-
min_length, max_length, min_value, max_value
|
223
|
-
)
|
224
|
-
|
225
|
-
min_length, max_length = calculate_bounds_1(
|
226
|
-
min_length, max_length, rand_config.min_length, rand_config.max_length
|
227
|
-
)
|
228
|
-
|
229
|
-
min_value, max_value = calculate_bounds_1(
|
230
|
-
min_value, max_value, rand_config.min_inclusive, rand_config.max_inclusive
|
231
|
-
)
|
232
|
-
|
233
|
-
logger.debug(
|
234
|
-
'restrictions after override: min_length: %4s; max_length: %4s; min_value: %4s; max_value: %4s',
|
235
|
-
min_length, max_length, min_value, max_value
|
236
|
-
)
|
237
|
-
|
238
|
-
generated_value = self._generate_value_by_type(
|
239
|
-
xsd_type, patterns,
|
240
|
-
min_length, max_length,
|
241
|
-
min_value, max_value,
|
242
|
-
total_digits, fraction_digits
|
243
|
-
)
|
244
289
|
logger.debug('value generated: "%s"', generated_value)
|
245
290
|
return generated_value
|
246
291
|
|
@@ -254,83 +299,27 @@ class XmlGenerator:
|
|
254
299
|
|
255
300
|
raise RuntimeError(f"Can't generate value - unhandled type. Target name: {target_name}")
|
256
301
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
if not
|
263
|
-
type_id = base_type.id
|
264
|
-
if not type_id:
|
265
|
-
type_id = xsd_type.root_type.id
|
266
|
-
|
267
|
-
logger.debug('generate value for type: "%s"', type_id)
|
268
|
-
|
269
|
-
match type_id:
|
270
|
-
case 'string':
|
271
|
-
return self._generate_string(patterns, min_length, max_length)
|
272
|
-
case 'boolean':
|
273
|
-
return self._generate_boolean()
|
274
|
-
case 'integer':
|
275
|
-
return self._generate_integer(total_digits, min_value, max_value)
|
276
|
-
case 'decimal':
|
277
|
-
return self._generate_decimal(total_digits, fraction_digits, min_value, max_value)
|
278
|
-
case 'float':
|
279
|
-
return self._generate_float(min_value, max_value)
|
280
|
-
case 'double':
|
281
|
-
return self._generate_double(min_value, max_value)
|
282
|
-
case 'duration':
|
283
|
-
return self._generate_duration()
|
284
|
-
case 'dateTime':
|
285
|
-
return self._generate_datetime()
|
286
|
-
case 'date':
|
287
|
-
return self._generate_date()
|
288
|
-
case 'time':
|
289
|
-
return self._generate_time()
|
290
|
-
case 'gYearMonth':
|
291
|
-
return self._generate_gyearmonth()
|
292
|
-
case 'gYear':
|
293
|
-
return self._generate_gyear()
|
294
|
-
case 'gMonthDay':
|
295
|
-
return self._generate_gmonthday()
|
296
|
-
case 'gDay':
|
297
|
-
return self._generate_gday()
|
298
|
-
case 'gMonth':
|
299
|
-
return self._generate_gmonth()
|
300
|
-
case 'hexBinary':
|
301
|
-
return self._generate_hex_binary()
|
302
|
-
case 'base64Binary':
|
303
|
-
return self._generate_base64_binary()
|
304
|
-
case 'anyURI':
|
305
|
-
return self._generate_any_uri()
|
306
|
-
case 'QName':
|
307
|
-
return self._generate_qname()
|
308
|
-
case 'NOTATION':
|
309
|
-
return self._generate_notation()
|
310
|
-
case _:
|
311
|
-
raise RuntimeError(type_id)
|
312
|
-
|
313
|
-
def _generate_string(self, patterns, min_length, max_length):
|
314
|
-
if patterns is not None:
|
302
|
+
# noinspection PyUnusedLocal
|
303
|
+
def _generate_boolean(self, constraints: TypeConstraints):
|
304
|
+
return self.randomizer.any(['true', 'false'])
|
305
|
+
|
306
|
+
def _generate_string(self, constraints: TypeConstraints):
|
307
|
+
if constraints.patterns is not None:
|
315
308
|
# Генерация строки по regex
|
316
|
-
random_enum = self.randomizer.any(patterns)
|
309
|
+
random_enum = self.randomizer.any(constraints.patterns)
|
317
310
|
random_pattern = random_enum.attrib['value']
|
318
311
|
return self.randomizer.regex(random_pattern)
|
319
312
|
|
320
313
|
# Иначе генерируем случайную строку
|
321
|
-
return self.randomizer.ascii_string(min_length, max_length)
|
322
|
-
|
323
|
-
def _generate_boolean(self):
|
324
|
-
return self.randomizer.any(['true', 'false'])
|
314
|
+
return self.randomizer.ascii_string(constraints.min_length, constraints.max_length)
|
325
315
|
|
326
|
-
def
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
316
|
+
def _generate_decimal(self, constraints: TypeConstraints):
|
317
|
+
rand_config = constraints.rand_config
|
318
|
+
min_value = constraints.min_value
|
319
|
+
max_value = constraints.max_value
|
320
|
+
total_digits = constraints.total_digits
|
321
|
+
fraction_digits = constraints.fraction_digits
|
332
322
|
|
333
|
-
def _generate_decimal(self, total_digits, fraction_digits, min_value, max_value):
|
334
323
|
if fraction_digits is None:
|
335
324
|
fraction_digits = self.randomizer.integer(1, 3)
|
336
325
|
|
@@ -345,112 +334,263 @@ class XmlGenerator:
|
|
345
334
|
|
346
335
|
integer_digits = total_digits - fraction_digits
|
347
336
|
|
348
|
-
# negative
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
337
|
+
# negative bound
|
338
|
+
digit_min = -(10 ** integer_digits - 1)
|
339
|
+
# positive bound
|
340
|
+
digit_max = 10 ** integer_digits - 1
|
341
|
+
logger.debug("integer digits: %s; digit_min: %s; digit_max: %s", integer_digits, digit_min, digit_max)
|
342
|
+
|
343
|
+
logger.debug('bounds before adjust: min_value: %4s; max_value: %4s', min_value, max_value)
|
344
|
+
config_min = rand_config.min_inclusive
|
345
|
+
config_max = rand_config.max_inclusive
|
346
|
+
effective_min, effective_max \
|
347
|
+
= merge_constraints(digit_min, digit_max, min_value, max_value, config_min, config_max)
|
348
|
+
logger.debug('bounds after adjust: min_value: %4s; max_value: %4s', effective_min, effective_max)
|
349
|
+
|
350
|
+
if fraction_digits == 0:
|
351
|
+
random_int = self.randomizer.integer(min_value, max_value)
|
352
|
+
return str(random_int)
|
353
|
+
else:
|
354
|
+
random_float = self.randomizer.float(effective_min, effective_max)
|
355
|
+
return f"{random_float:.{fraction_digits}f}"
|
358
356
|
|
359
|
-
def _generate_float(self,
|
360
|
-
|
357
|
+
def _generate_float(self, constraints: TypeConstraints):
|
358
|
+
decimal_constraints = replace(constraints, fraction_digits=2)
|
359
|
+
return self._generate_decimal(decimal_constraints)
|
361
360
|
|
362
|
-
def _generate_double(self,
|
363
|
-
|
361
|
+
def _generate_double(self, constraints: TypeConstraints):
|
362
|
+
decimal_constraints = replace(constraints, fraction_digits=2)
|
363
|
+
return self._generate_decimal(decimal_constraints)
|
364
364
|
|
365
|
-
def _generate_duration(self):
|
365
|
+
def _generate_duration(self, constraints: TypeConstraints):
|
366
366
|
raise RuntimeError("not yet implemented")
|
367
367
|
|
368
|
-
|
368
|
+
# noinspection PyUnusedLocal
|
369
|
+
def _generate_datetime(self, constraints: TypeConstraints):
|
369
370
|
random_datetime = self.randomizer.random_datetime()
|
370
371
|
formatted = random_datetime.isoformat()
|
371
372
|
return formatted
|
372
373
|
|
373
|
-
|
374
|
+
# noinspection PyUnusedLocal
|
375
|
+
def _generate_date(self, constraints: TypeConstraints):
|
374
376
|
random_date = self.randomizer.random_date()
|
375
377
|
formatted = random_date.isoformat()
|
376
378
|
return formatted
|
377
379
|
|
378
|
-
|
380
|
+
# noinspection PyUnusedLocal
|
381
|
+
def _generate_time(self, constraints: TypeConstraints):
|
379
382
|
random_time = self.randomizer.random_time()
|
380
383
|
formatted = random_time.isoformat()
|
381
384
|
return formatted
|
382
385
|
|
383
|
-
|
386
|
+
# noinspection PyUnusedLocal
|
387
|
+
def _generate_gyearmonth(self, constraints: TypeConstraints):
|
384
388
|
random_date = self.randomizer.random_date()
|
385
389
|
formatted = random_date.strftime('%Y-%m')
|
386
390
|
return formatted
|
387
391
|
|
388
|
-
|
392
|
+
# noinspection PyUnusedLocal
|
393
|
+
def _generate_gyear(self, constraints: TypeConstraints):
|
389
394
|
return str(self.randomizer.integer(2000, 2050))
|
390
395
|
|
391
|
-
|
396
|
+
# noinspection PyUnusedLocal
|
397
|
+
def _generate_gmonthday(self, constraints: TypeConstraints):
|
392
398
|
random_date = self.randomizer.random_date()
|
393
399
|
formatted = random_date.strftime('--%m-%d')
|
394
400
|
return formatted
|
395
401
|
|
396
|
-
|
402
|
+
# noinspection PyUnusedLocal
|
403
|
+
def _generate_gday(self, constraints: TypeConstraints):
|
397
404
|
random_date = self.randomizer.random_date()
|
398
405
|
formatted = random_date.strftime('---%d')
|
399
406
|
return formatted
|
400
407
|
|
401
|
-
|
408
|
+
# noinspection PyUnusedLocal
|
409
|
+
def _generate_gmonth(self, constraints: TypeConstraints):
|
402
410
|
random_date = self.randomizer.random_date()
|
403
411
|
formatted = random_date.strftime('--%m--')
|
404
412
|
return formatted
|
405
413
|
|
406
|
-
def _generate_hex_binary(self):
|
407
|
-
|
414
|
+
def _generate_hex_binary(self, constraints: TypeConstraints):
|
415
|
+
return self.randomizer.hex_string(constraints.min_length, constraints.max_length)
|
408
416
|
|
409
|
-
|
417
|
+
# noinspection PyUnusedLocal
|
418
|
+
def _generate_base64_binary(self, constraints: TypeConstraints):
|
410
419
|
raise RuntimeError("not yet implemented")
|
411
420
|
|
412
|
-
|
421
|
+
# noinspection PyUnusedLocal
|
422
|
+
def _generate_any_uri(self, constraints: TypeConstraints):
|
413
423
|
raise RuntimeError("not yet implemented")
|
414
424
|
|
415
|
-
|
425
|
+
# noinspection PyUnusedLocal
|
426
|
+
def _generate_qname(self, constraints: TypeConstraints):
|
416
427
|
raise RuntimeError("not yet implemented")
|
417
428
|
|
418
|
-
|
429
|
+
# noinspection PyUnusedLocal
|
430
|
+
def _generate_notation(self, constraints: TypeConstraints):
|
419
431
|
raise RuntimeError("not yet implemented")
|
420
432
|
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
433
|
+
def _generate_byte(self, constraints: TypeConstraints):
|
434
|
+
constraints = replace(constraints, fraction_digits=0)
|
435
|
+
return self._generate_decimal(constraints)
|
436
|
+
|
437
|
+
def _generate_short(self, constraints: TypeConstraints):
|
438
|
+
constraints = replace(constraints, fraction_digits=0)
|
439
|
+
return self._generate_decimal(constraints)
|
440
|
+
|
441
|
+
def _generate_int(self, constraints: TypeConstraints):
|
442
|
+
constraints = replace(constraints, fraction_digits=0)
|
443
|
+
return self._generate_decimal(constraints)
|
444
|
+
|
445
|
+
def _generate_integer(self, constraints: TypeConstraints):
|
446
|
+
min_value = constraints.min_value if constraints.min_value is not None else -2147483648
|
447
|
+
max_value = constraints.max_value if constraints.max_value is not None else 2147483647
|
448
|
+
constraints = replace(constraints, min_value=min_value, max_value=max_value, fraction_digits=0)
|
449
|
+
return self._generate_decimal(constraints)
|
450
|
+
|
451
|
+
def _generate_long(self, constraints: TypeConstraints):
|
452
|
+
constraints = replace(constraints, fraction_digits=0)
|
453
|
+
return self._generate_decimal(constraints)
|
454
|
+
|
455
|
+
def _generate_unsigned_byte(self, constraints: TypeConstraints):
|
456
|
+
constraints = replace(constraints, fraction_digits=0)
|
457
|
+
return self._generate_decimal(constraints)
|
458
|
+
|
459
|
+
def _generate_unsigned_short(self, constraints: TypeConstraints):
|
460
|
+
constraints = replace(constraints, fraction_digits=0)
|
461
|
+
return self._generate_decimal(constraints)
|
462
|
+
|
463
|
+
def _generate_unsigned_int(self, constraints: TypeConstraints):
|
464
|
+
constraints = replace(constraints, fraction_digits=0)
|
465
|
+
return self._generate_decimal(constraints)
|
466
|
+
|
467
|
+
def _generate_unsigned_long(self, constraints: TypeConstraints):
|
468
|
+
constraints = replace(constraints, fraction_digits=0)
|
469
|
+
return self._generate_decimal(constraints)
|
470
|
+
|
471
|
+
def _generate_positive_integer(self, constraints: TypeConstraints):
|
472
|
+
min_value = constraints.min_value if constraints.min_value is not None else 1
|
473
|
+
max_value = constraints.max_value if constraints.max_value is not None else 2 ** 31 - 1
|
474
|
+
constraints = replace(constraints, min_value=min_value, max_value=max_value, fraction_digits=0)
|
475
|
+
return self._generate_decimal(constraints)
|
476
|
+
|
477
|
+
def _generate_negative_integer(self, constraints: TypeConstraints):
|
478
|
+
min_value = constraints.min_value if constraints.min_value is not None else -2 ** 31
|
479
|
+
max_value = constraints.max_value if constraints.max_value is not None else -1
|
480
|
+
constraints = replace(constraints, min_value=min_value, max_value=max_value, fraction_digits=0)
|
481
|
+
return self._generate_decimal(constraints)
|
482
|
+
|
483
|
+
def _generate_non_positive_integer(self, constraints: TypeConstraints):
|
484
|
+
min_value = constraints.min_value if constraints.min_value is not None else -2 ** 31
|
485
|
+
max_value = constraints.max_value if constraints.max_value is not None else 0
|
486
|
+
constraints = replace(constraints, min_value=min_value, max_value=max_value, fraction_digits=0)
|
487
|
+
return self._generate_decimal(constraints)
|
488
|
+
|
489
|
+
def _generate_non_negative_integer(self, constraints: TypeConstraints):
|
490
|
+
min_value = constraints.min_value if constraints.min_value is not None else 0
|
491
|
+
max_value = constraints.max_value if constraints.max_value is not None else 2 ** 31 - 1
|
492
|
+
constraints = replace(constraints, min_value=min_value, max_value=max_value, fraction_digits=0)
|
493
|
+
return self._generate_decimal(constraints)
|
494
|
+
|
495
|
+
|
496
|
+
def extract_type_constraints(xsd_type, local_config: GeneratorConfig) -> TypeConstraints:
|
497
|
+
min_length = getattr(xsd_type, 'min_length', None)
|
498
|
+
max_length = getattr(xsd_type, 'max_length', None)
|
499
|
+
min_value = getattr(xsd_type, 'min_value', None)
|
500
|
+
max_value = getattr(xsd_type, 'max_value', None)
|
501
|
+
total_digits = None
|
502
|
+
fraction_digits = None
|
503
|
+
patterns = getattr(xsd_type, 'patterns', None)
|
504
|
+
validators = getattr(xsd_type, 'validators', None)
|
505
|
+
for validator in validators:
|
506
|
+
if isinstance(validator, XsdMinExclusiveFacet):
|
507
|
+
min_value = validator.value
|
508
|
+
elif isinstance(validator, XsdMinInclusiveFacet):
|
509
|
+
min_value = validator.value
|
510
|
+
elif isinstance(validator, XsdMaxExclusiveFacet):
|
511
|
+
max_value = validator.value
|
512
|
+
elif isinstance(validator, XsdMaxInclusiveFacet):
|
513
|
+
max_value = validator.value
|
514
|
+
elif isinstance(validator, XsdLengthFacet):
|
515
|
+
min_length = validator.value
|
516
|
+
max_length = validator.value
|
517
|
+
elif isinstance(validator, XsdMinLengthFacet):
|
518
|
+
min_length = validator.value
|
519
|
+
elif isinstance(validator, XsdMaxLengthFacet):
|
520
|
+
max_length = validator.value
|
521
|
+
elif isinstance(validator, XsdTotalDigitsFacet):
|
522
|
+
total_digits = validator.value
|
523
|
+
elif isinstance(validator, XsdFractionDigitsFacet):
|
524
|
+
fraction_digits = validator.value
|
525
|
+
elif isinstance(validator, XsdEnumerationFacets):
|
526
|
+
pass
|
527
|
+
elif callable(validator):
|
528
|
+
pass
|
434
529
|
else:
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
if
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
530
|
+
raise RuntimeError(f"Unhandled validator: {validator}")
|
531
|
+
|
532
|
+
if isinstance(min_value, Decimal):
|
533
|
+
min_value = float(min_value)
|
534
|
+
if isinstance(max_value, Decimal):
|
535
|
+
max_value = float(max_value)
|
536
|
+
|
537
|
+
rand_config = local_config.randomization
|
538
|
+
|
539
|
+
logger.debug('bounds before adjust: min_length: %4s; max_length: %4s', min_length, max_length)
|
540
|
+
min_length, max_length = merge_constraints(
|
541
|
+
schema_min=min_length,
|
542
|
+
schema_max=max_length,
|
543
|
+
config_min=rand_config.min_length,
|
544
|
+
config_max=rand_config.max_length
|
545
|
+
)
|
546
|
+
logger.debug('bounds after adjust: min_length: %4s; max_length: %4s', min_length, max_length)
|
547
|
+
|
548
|
+
return TypeConstraints(
|
549
|
+
min_length=min_length,
|
550
|
+
max_length=max_length,
|
551
|
+
min_value=min_value,
|
552
|
+
max_value=max_value,
|
553
|
+
total_digits=total_digits,
|
554
|
+
fraction_digits=fraction_digits,
|
555
|
+
patterns=patterns,
|
556
|
+
rand_config=rand_config
|
557
|
+
)
|
558
|
+
|
559
|
+
|
560
|
+
def merge_constraints(digit_min=None, digit_max=None, schema_min=None, schema_max=None, config_min=None, config_max=None):
|
561
|
+
logger.debug(
|
562
|
+
"merge numeric constraints: "
|
563
|
+
"digit_min: %s, digit_max: %s, schema_min: %s, schema_max: %s, config_min: %s, config_max: %s",
|
564
|
+
digit_min, digit_max, schema_min, schema_max, config_min, config_max)
|
565
|
+
|
566
|
+
# За основу берем цифровые ограничения (они самые нестрогие)
|
567
|
+
effective_min, effective_max = digit_min, digit_max
|
568
|
+
|
569
|
+
# Применяем схемные ограничения
|
570
|
+
if schema_min is not None:
|
571
|
+
effective_min = max(effective_min, schema_min) if effective_min is not None else schema_min
|
572
|
+
if schema_max is not None:
|
573
|
+
effective_max = min(effective_max, schema_max) if effective_max is not None else schema_max
|
574
|
+
|
575
|
+
# Применяем конфигурационные ограничения с проверкой на конфликт
|
446
576
|
if config_min is not None:
|
447
|
-
|
448
|
-
|
449
|
-
|
577
|
+
if effective_max is not None and config_min > effective_max:
|
578
|
+
logger.warning("can't apply bound from configuration: config_min (%s) > effective_max (%s)",
|
579
|
+
config_min, effective_max)
|
580
|
+
else:
|
581
|
+
effective_min = max(effective_min, config_min) if effective_min is not None else config_min
|
450
582
|
|
451
583
|
if config_max is not None:
|
452
|
-
|
453
|
-
|
454
|
-
|
584
|
+
if effective_min is not None and config_max < effective_min:
|
585
|
+
logger.warning("can't apply bound from configuration: config_max (%s) < effective_min (%s)",
|
586
|
+
config_max, effective_min)
|
587
|
+
else:
|
588
|
+
effective_max = min(effective_max, config_max) if effective_max is not None else config_max
|
589
|
+
|
590
|
+
# Проверяем на конфликт
|
591
|
+
if effective_min is not None and effective_max is not None and effective_min > effective_max:
|
592
|
+
logger.warning("constrains conflict: effective_min (%s) > effective_max (%s). Swap values.",
|
593
|
+
effective_min, effective_max)
|
594
|
+
effective_min, effective_max = effective_max, effective_min
|
455
595
|
|
456
|
-
return
|
596
|
+
return effective_min, effective_max
|
xmlgenerator/randomization.py
CHANGED
@@ -4,7 +4,6 @@ import re
|
|
4
4
|
import string
|
5
5
|
import sys
|
6
6
|
from datetime import datetime, date, time, timedelta
|
7
|
-
from decimal import Decimal
|
8
7
|
|
9
8
|
import rstr
|
10
9
|
from faker import Faker
|
@@ -42,10 +41,6 @@ class Randomizer:
|
|
42
41
|
return self._rnd.randint(min_value, max_value)
|
43
42
|
|
44
43
|
def float(self, min_value, max_value):
|
45
|
-
if isinstance(min_value, Decimal):
|
46
|
-
min_value = float(min_value)
|
47
|
-
if isinstance(max_value, Decimal):
|
48
|
-
max_value = float(max_value)
|
49
44
|
return self._rnd.uniform(min_value, max_value)
|
50
45
|
|
51
46
|
def ascii_string(self, min_length, max_length):
|
@@ -58,6 +53,16 @@ class Randomizer:
|
|
58
53
|
letters = string.ascii_lowercase
|
59
54
|
return ''.join(self._rnd.choice(letters) for _ in range(length)).capitalize()
|
60
55
|
|
56
|
+
def hex_string(self, min_length, max_length):
|
57
|
+
if min_length is None:
|
58
|
+
min_length = 1
|
59
|
+
if max_length is None:
|
60
|
+
max_length = 20
|
61
|
+
|
62
|
+
length = self._rnd.randint(min_length, max_length)
|
63
|
+
circumflexes = ''.join('^' for _ in range(length))
|
64
|
+
return self._fake.hexify(text=circumflexes, upper=True)
|
65
|
+
|
61
66
|
def random_date(self, start_date: str = '1990-01-01', end_date: str = '2025-12-31') -> date:
|
62
67
|
start = date.fromisoformat(start_date)
|
63
68
|
end = date.fromisoformat(end_date)
|
@@ -138,3 +143,6 @@ class Randomizer:
|
|
138
143
|
def snils_formatted(self):
|
139
144
|
snils = self._fake.snils()
|
140
145
|
return f"{snils[:3]}-{snils[3:6]}-{snils[6:9]} {snils[9:]}"
|
146
|
+
|
147
|
+
def email(self):
|
148
|
+
return self._fake.email()
|
xmlgenerator/substitution.py
CHANGED
@@ -46,6 +46,7 @@ class Substitutor:
|
|
46
46
|
'ogrn_fl': lambda args: self.randomizer.ogrn_fl(),
|
47
47
|
'kpp': lambda args: self.randomizer.kpp(),
|
48
48
|
'snils_formatted': lambda args: self.randomizer.snils_formatted(),
|
49
|
+
'email': lambda args: self.randomizer.email(),
|
49
50
|
}
|
50
51
|
|
51
52
|
def reset_context(self, xsd_filename, config_local):
|
@@ -61,7 +62,7 @@ class Substitutor:
|
|
61
62
|
resolved_value = self._process_expression(output_filename)
|
62
63
|
self._local_context['output_filename'] = resolved_value
|
63
64
|
|
64
|
-
logger.debug('
|
65
|
+
logger.debug('reset local context...')
|
65
66
|
logger.debug('local_context["source_filename"] = %s', xsd_filename)
|
66
67
|
logger.debug('local_context["source_extracted"] = %s (extracted with regexp %s)', source_extracted, source_filename)
|
67
68
|
logger.debug('local_context["output_filename"] = %s', resolved_value)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: xmlgenerator
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.5.0
|
4
4
|
Summary: Generates XML documents from XSD schemas
|
5
5
|
Home-page: https://github.com/lexakimov/xmlgenerator
|
6
6
|
Author: Alexey Akimov
|
@@ -173,6 +173,8 @@ global:
|
|
173
173
|
# Probability of adding optional elements (0.0-1.0)
|
174
174
|
# Default value: 0.5
|
175
175
|
probability: 1
|
176
|
+
# Limit for the minimal number of elements
|
177
|
+
min_occurs: 0
|
176
178
|
# Limit for the maximum number of elements
|
177
179
|
max_occurs: 5
|
178
180
|
# Minimum string length
|
@@ -268,6 +270,7 @@ In the `value_override` sections, you can specify either a string value or speci
|
|
268
270
|
| `ogrn_fl` | Primary State Registration Number (Physical Person) |
|
269
271
|
| `kpp` | Reason Code for Registration |
|
270
272
|
| `snils_formatted` | SNILS (Personal Insurance Account Number) in the format `123-456-789 90` |
|
273
|
+
| `email` | Random email address |
|
271
274
|
|
272
275
|
**Configuration Examples:**
|
273
276
|
|
@@ -0,0 +1,14 @@
|
|
1
|
+
xmlgenerator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
xmlgenerator/arguments.py,sha256=Pf7Bccan0FeO6v5INpBkhLlGfJg-FGMRX5pG8E2KVgo,4834
|
3
|
+
xmlgenerator/bootstrap.py,sha256=T_Xy5PElb75EuyKIwXUGkQ2mntt3v2RwC1ulFI-CZnM,3654
|
4
|
+
xmlgenerator/configuration.py,sha256=JYhz_lONxd0faUiZHG-TVEs6yocn0s__Ulwtcvq9eDs,5946
|
5
|
+
xmlgenerator/generator.py,sha256=vECUZZ5VlMq3Mpam3_ZUsWmzHr402nqxDRKzLNAqNiU,29237
|
6
|
+
xmlgenerator/randomization.py,sha256=azXW1SxKSA9_lw1IBQDPOwSUXFEXo8IGWFD0an-eVF0,4416
|
7
|
+
xmlgenerator/substitution.py,sha256=v4rzqnF1p1yN0VKRDFwQM5zQbpdg9ebbrh65cnh9qxw,6078
|
8
|
+
xmlgenerator/validation.py,sha256=uCJjS5YmRDlAp9C-5Rd4E2Brh6_3WOG2-dSGxDiaH14,2023
|
9
|
+
xmlgenerator-0.5.0.dist-info/licenses/LICENSE,sha256=QlXK8O3UcoAYUYwVJNgB9MSM7O94ogNo_1hd9GzznUQ,1070
|
10
|
+
xmlgenerator-0.5.0.dist-info/METADATA,sha256=-zApCcYPrg5A8nC2CLwBHWFEwMOwBDni21AnFhIEd1I,13083
|
11
|
+
xmlgenerator-0.5.0.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
|
12
|
+
xmlgenerator-0.5.0.dist-info/entry_points.txt,sha256=ly9hKr3o4AzFUkelBZNRzyKYf-Ld4kfcffvBu1oHq54,61
|
13
|
+
xmlgenerator-0.5.0.dist-info/top_level.txt,sha256=jr7FbMBm8MQ6j8I_-nWzQQEseXzwSCZNXgrkWuk9P4E,13
|
14
|
+
xmlgenerator-0.5.0.dist-info/RECORD,,
|
@@ -1,14 +0,0 @@
|
|
1
|
-
xmlgenerator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
xmlgenerator/arguments.py,sha256=0WHKt7eOS7M3_R-rYdp_52Q8rgArCF9VlIDkPVP_8dk,4784
|
3
|
-
xmlgenerator/bootstrap.py,sha256=7ONv9Eh46z6xd8w_V7FO1156FonAX-LHYpRrQ44PrBU,3668
|
4
|
-
xmlgenerator/configuration.py,sha256=JYhz_lONxd0faUiZHG-TVEs6yocn0s__Ulwtcvq9eDs,5946
|
5
|
-
xmlgenerator/generator.py,sha256=eBW8UlY8Bu8xh4oo5jp4_yo6OKz9T3xvQeZYC66lui4,20814
|
6
|
-
xmlgenerator/randomization.py,sha256=ekNQJYgcmDCf6uCYiZnWat7u_9kO6TAQQ8qZFIpiB7o,4205
|
7
|
-
xmlgenerator/substitution.py,sha256=1nvjQLSUS9Yo8r2T3f420Upbwm6iikUQG3lG5TQUSDU,6016
|
8
|
-
xmlgenerator/validation.py,sha256=uCJjS5YmRDlAp9C-5Rd4E2Brh6_3WOG2-dSGxDiaH14,2023
|
9
|
-
xmlgenerator-0.3.0.dist-info/licenses/LICENSE,sha256=QlXK8O3UcoAYUYwVJNgB9MSM7O94ogNo_1hd9GzznUQ,1070
|
10
|
-
xmlgenerator-0.3.0.dist-info/METADATA,sha256=ONyx3zcbuX1zQTnt-5ORWcxU1shJDYbgsyFBlEWx8E4,12870
|
11
|
-
xmlgenerator-0.3.0.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
|
12
|
-
xmlgenerator-0.3.0.dist-info/entry_points.txt,sha256=ly9hKr3o4AzFUkelBZNRzyKYf-Ld4kfcffvBu1oHq54,61
|
13
|
-
xmlgenerator-0.3.0.dist-info/top_level.txt,sha256=jr7FbMBm8MQ6j8I_-nWzQQEseXzwSCZNXgrkWuk9P4E,13
|
14
|
-
xmlgenerator-0.3.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|