xmlgenerator 0.3.0__py3-none-any.whl → 0.4.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/bootstrap.py +1 -1
- xmlgenerator/generator.py +181 -157
- xmlgenerator/randomization.py +0 -5
- xmlgenerator/substitution.py +1 -1
- {xmlgenerator-0.3.0.dist-info → xmlgenerator-0.4.0.dist-info}/METADATA +3 -1
- xmlgenerator-0.4.0.dist-info/RECORD +14 -0
- xmlgenerator-0.3.0.dist-info/RECORD +0 -14
- {xmlgenerator-0.3.0.dist-info → xmlgenerator-0.4.0.dist-info}/WHEEL +0 -0
- {xmlgenerator-0.3.0.dist-info → xmlgenerator-0.4.0.dist-info}/entry_points.txt +0 -0
- {xmlgenerator-0.3.0.dist-info → xmlgenerator-0.4.0.dist-info}/licenses/LICENSE +0 -0
- {xmlgenerator-0.3.0.dist-info → xmlgenerator-0.4.0.dist-info}/top_level.txt +0 -0
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,6 @@
|
|
1
1
|
import logging
|
2
|
+
from decimal import Decimal
|
2
3
|
|
3
|
-
import xmlschema
|
4
4
|
from lxml import etree
|
5
5
|
from xmlschema.validators import XsdComplexType, XsdAtomicRestriction, XsdTotalDigitsFacet, XsdElement, \
|
6
6
|
XsdGroup, XsdFractionDigitsFacet, XsdLengthFacet, XsdMaxLengthFacet, XsdMinExclusiveFacet, XsdMinInclusiveFacet, \
|
@@ -18,7 +18,8 @@ class XmlGenerator:
|
|
18
18
|
self.randomizer = randomizer
|
19
19
|
self.substitutor = substitutor
|
20
20
|
|
21
|
-
def generate_xml(self, xsd_schema
|
21
|
+
def generate_xml(self, xsd_schema, local_config: GeneratorConfig) -> etree.Element:
|
22
|
+
logger.debug('generate xml document...')
|
22
23
|
ns_map = {None if k == '' else k: v for k, v in xsd_schema.namespaces.items() if v != ''}
|
23
24
|
xsd_root_element = xsd_schema.root_elements[0]
|
24
25
|
xml_root_element = etree.Element(xsd_root_element.name, nsmap=ns_map)
|
@@ -26,7 +27,11 @@ class XmlGenerator:
|
|
26
27
|
self._add_elements(xml_tree, xml_root_element, xsd_root_element, local_config)
|
27
28
|
return xml_root_element
|
28
29
|
|
29
|
-
def _add_elements(self, xml_tree, xml_element
|
30
|
+
def _add_elements(self, xml_tree, xml_element, xsd_element, local_config: GeneratorConfig) -> None:
|
31
|
+
rand_config = local_config.randomization
|
32
|
+
min_occurs_conf = rand_config.min_occurs
|
33
|
+
max_occurs_conf = rand_config.max_occurs
|
34
|
+
|
30
35
|
# Process child elements --------------------------------------------------------------------------------------
|
31
36
|
if isinstance(xsd_element, XsdElement):
|
32
37
|
element_xpath = xml_tree.getpath(xml_element)
|
@@ -38,14 +43,14 @@ class XmlGenerator:
|
|
38
43
|
attributes = getattr(xsd_element, 'attributes', dict())
|
39
44
|
if len(attributes) > 0 and xsd_element_type.local_name != 'anyType':
|
40
45
|
for attr_name, attr in attributes.items():
|
41
|
-
logger.debug('element: %s; attribute "%s" [processing]', element_xpath, attr_name)
|
46
|
+
logger.debug('element: %s; attribute: "%s" - [processing]', element_xpath, attr_name)
|
42
47
|
use = attr.use # optional | required | prohibited
|
43
48
|
if use == 'prohibited':
|
44
|
-
logger.debug('element: %s; attribute: "%s" [skipped]', element_xpath, attr_name)
|
49
|
+
logger.debug('element: %s; attribute: "%s" - [skipped]', element_xpath, attr_name)
|
45
50
|
continue
|
46
51
|
elif use == 'optional':
|
47
|
-
if self.randomizer.random() >
|
48
|
-
logger.debug('element: %s; attribute: "%s" [skipped]', element_xpath, attr_name)
|
52
|
+
if self.randomizer.random() > rand_config.probability:
|
53
|
+
logger.debug('element: %s; attribute: "%s" - [skipped]', element_xpath, attr_name)
|
49
54
|
continue
|
50
55
|
|
51
56
|
attr_value = self._generate_value(attr.type, attr_name, local_config)
|
@@ -57,58 +62,82 @@ class XmlGenerator:
|
|
57
62
|
text = self._generate_value(xsd_element_type, xsd_element.name, local_config)
|
58
63
|
xml_element.text = text
|
59
64
|
logger.debug('element: %s = "%s"', element_xpath, text)
|
60
|
-
|
65
|
+
|
61
66
|
elif isinstance(xsd_element_type, XsdAtomicRestriction):
|
62
67
|
text = self._generate_value(xsd_element_type, xsd_element.name, local_config)
|
63
68
|
xml_element.text = text
|
64
69
|
logger.debug('element: %s = "%s"', element_xpath, text)
|
65
|
-
|
70
|
+
|
66
71
|
elif isinstance(xsd_element_type, XsdComplexType):
|
67
72
|
xsd_element_type_content = xsd_element_type.content
|
68
73
|
if isinstance(xsd_element_type_content, XsdGroup):
|
69
74
|
self._add_elements(xml_tree, xml_element, xsd_element_type_content, local_config)
|
70
75
|
else:
|
71
76
|
raise RuntimeError()
|
77
|
+
|
72
78
|
else:
|
73
79
|
raise RuntimeError()
|
74
80
|
|
75
81
|
elif isinstance(xsd_element, XsdGroup):
|
76
82
|
model = xsd_element.model
|
77
83
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
84
|
+
min_occurs = getattr(xsd_element, 'min_occurs', None)
|
85
|
+
max_occurs = getattr(xsd_element, 'max_occurs', None)
|
86
|
+
min_occurs, max_occurs = merge_constraints(
|
87
|
+
schema_min=min_occurs,
|
88
|
+
schema_max=max_occurs,
|
89
|
+
config_min=min_occurs_conf,
|
90
|
+
config_max=max_occurs_conf
|
91
|
+
)
|
92
|
+
if max_occurs is None:
|
93
|
+
max_occurs = 10
|
94
|
+
group_occurs = self.randomizer.integer(min_occurs, max_occurs)
|
95
|
+
logger.debug('add %s (random between %s and %s) groups of type "%s"',
|
96
|
+
group_occurs, min_occurs, max_occurs, model)
|
83
97
|
|
84
98
|
if model == 'all':
|
85
99
|
for _ in range(group_occurs):
|
86
100
|
xsd_group_content = xsd_element.content
|
87
101
|
for xsd_child_element_type in xsd_group_content:
|
88
102
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
103
|
+
min_occurs = getattr(xsd_child_element_type, 'min_occurs', None)
|
104
|
+
max_occurs = getattr(xsd_child_element_type, 'max_occurs', None)
|
105
|
+
min_occurs, max_occurs = merge_constraints(
|
106
|
+
schema_min=min_occurs,
|
107
|
+
schema_max=max_occurs,
|
108
|
+
config_min=min_occurs_conf,
|
109
|
+
config_max=max_occurs_conf
|
110
|
+
)
|
111
|
+
if max_occurs is None:
|
112
|
+
max_occurs = 10
|
113
|
+
element_occurs = self.randomizer.integer(min_occurs, max_occurs)
|
114
|
+
logger.debug('element_occurs: %s (random between %s and %s)', element_occurs, min_occurs,
|
115
|
+
max_occurs)
|
94
116
|
|
95
117
|
for _ in range(element_occurs):
|
96
118
|
xml_child_element = etree.SubElement(xml_element, xsd_child_element_type.name)
|
97
119
|
self._add_elements(xml_tree, xml_child_element, xsd_child_element_type, local_config)
|
98
|
-
return
|
99
120
|
|
100
121
|
elif model == 'sequence':
|
101
122
|
for _ in range(group_occurs):
|
102
123
|
xsd_group_content = xsd_element.content
|
103
124
|
for xsd_child_element_type in xsd_group_content:
|
125
|
+
if isinstance(xsd_child_element_type, XsdElement):
|
104
126
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
127
|
+
min_occurs = getattr(xsd_child_element_type, 'min_occurs', None)
|
128
|
+
max_occurs = getattr(xsd_child_element_type, 'max_occurs', None)
|
129
|
+
min_occurs, max_occurs = merge_constraints(
|
130
|
+
schema_min=min_occurs,
|
131
|
+
schema_max=max_occurs,
|
132
|
+
config_min=min_occurs_conf,
|
133
|
+
config_max=max_occurs_conf
|
134
|
+
)
|
135
|
+
if max_occurs is None:
|
136
|
+
max_occurs = 10
|
137
|
+
element_occurs = self.randomizer.integer(min_occurs, max_occurs)
|
138
|
+
logger.debug('element_occurs: %s (random between %s and %s)', element_occurs, min_occurs,
|
139
|
+
max_occurs)
|
110
140
|
|
111
|
-
if isinstance(xsd_child_element_type, XsdElement):
|
112
141
|
for _ in range(element_occurs):
|
113
142
|
xml_child_element = etree.SubElement(xml_element, xsd_child_element_type.name)
|
114
143
|
self._add_elements(xml_tree, xml_child_element, xsd_child_element_type, local_config)
|
@@ -123,22 +152,28 @@ class XmlGenerator:
|
|
123
152
|
|
124
153
|
else:
|
125
154
|
raise RuntimeError(xsd_child_element_type)
|
126
|
-
return
|
127
155
|
|
128
156
|
elif model == 'choice':
|
129
157
|
for _ in range(group_occurs):
|
130
158
|
xsd_child_element_type = self.randomizer.any(xsd_element)
|
131
159
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
160
|
+
min_occurs = getattr(xsd_child_element_type, 'min_occurs', None)
|
161
|
+
max_occurs = getattr(xsd_child_element_type, 'max_occurs', None)
|
162
|
+
min_occurs, max_occurs = merge_constraints(
|
163
|
+
schema_min=min_occurs,
|
164
|
+
schema_max=max_occurs,
|
165
|
+
config_min=min_occurs_conf,
|
166
|
+
config_max=max_occurs_conf
|
167
|
+
)
|
168
|
+
if max_occurs is None:
|
169
|
+
max_occurs = 10
|
170
|
+
element_occurs = self.randomizer.integer(min_occurs, max_occurs)
|
171
|
+
logger.debug('element_occurs: %s (random between %s and %s)', element_occurs, min_occurs,
|
172
|
+
max_occurs)
|
137
173
|
|
138
174
|
for _ in range(element_occurs):
|
139
175
|
xml_child_element = etree.SubElement(xml_element, xsd_child_element_type.name)
|
140
176
|
self._add_elements(xml_tree, xml_child_element, xsd_child_element_type, local_config)
|
141
|
-
return
|
142
177
|
|
143
178
|
else:
|
144
179
|
raise RuntimeError()
|
@@ -215,32 +250,69 @@ class XmlGenerator:
|
|
215
250
|
else:
|
216
251
|
raise RuntimeError(f"Unhandled validator: {validator}")
|
217
252
|
|
218
|
-
|
253
|
+
if isinstance(min_value, Decimal):
|
254
|
+
min_value = float(min_value)
|
255
|
+
if isinstance(max_value, Decimal):
|
256
|
+
max_value = float(max_value)
|
219
257
|
|
220
|
-
|
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
|
-
)
|
258
|
+
rand_config = local_config.randomization
|
237
259
|
|
238
|
-
|
239
|
-
|
240
|
-
min_length,
|
241
|
-
|
242
|
-
|
260
|
+
logger.debug('bounds before adjust: min_length: %4s; max_length: %4s', min_length, max_length)
|
261
|
+
min_length, max_length = merge_constraints(
|
262
|
+
schema_min=min_length,
|
263
|
+
schema_max=max_length,
|
264
|
+
config_min=rand_config.min_length,
|
265
|
+
config_max=rand_config.max_length
|
243
266
|
)
|
267
|
+
logger.debug('bounds after adjust: min_length: %4s; max_length: %4s', min_length, max_length)
|
268
|
+
|
269
|
+
type_id = xsd_type.id or xsd_type.base_type.id or xsd_type.root_type.id
|
270
|
+
logger.debug('generate value for type: "%s"', type_id)
|
271
|
+
|
272
|
+
match type_id:
|
273
|
+
case 'boolean':
|
274
|
+
result = self._generate_boolean()
|
275
|
+
case 'string':
|
276
|
+
result = self._generate_string(min_length, max_length, patterns)
|
277
|
+
case 'integer':
|
278
|
+
result = self._generate_integer(min_value, max_value, total_digits)
|
279
|
+
case 'decimal':
|
280
|
+
result = self._generate_decimal(rand_config, min_value, max_value, total_digits, fraction_digits)
|
281
|
+
case 'float':
|
282
|
+
result = self._generate_float(rand_config, min_value, max_value)
|
283
|
+
case 'double':
|
284
|
+
result = self._generate_double(rand_config, min_value, max_value)
|
285
|
+
case 'duration':
|
286
|
+
result = self._generate_duration()
|
287
|
+
case 'dateTime':
|
288
|
+
result = self._generate_datetime()
|
289
|
+
case 'date':
|
290
|
+
result = self._generate_date()
|
291
|
+
case 'time':
|
292
|
+
result = self._generate_time()
|
293
|
+
case 'gYearMonth':
|
294
|
+
result = self._generate_gyearmonth()
|
295
|
+
case 'gYear':
|
296
|
+
result = self._generate_gyear()
|
297
|
+
case 'gMonthDay':
|
298
|
+
result = self._generate_gmonthday()
|
299
|
+
case 'gDay':
|
300
|
+
result = self._generate_gday()
|
301
|
+
case 'gMonth':
|
302
|
+
result = self._generate_gmonth()
|
303
|
+
case 'hexBinary':
|
304
|
+
result = self._generate_hex_binary()
|
305
|
+
case 'base64Binary':
|
306
|
+
result = self._generate_base64_binary()
|
307
|
+
case 'anyURI':
|
308
|
+
result = self._generate_any_uri()
|
309
|
+
case 'QName':
|
310
|
+
result = self._generate_qname()
|
311
|
+
case 'NOTATION':
|
312
|
+
result = self._generate_notation()
|
313
|
+
case _:
|
314
|
+
raise RuntimeError(type_id)
|
315
|
+
generated_value = result
|
244
316
|
logger.debug('value generated: "%s"', generated_value)
|
245
317
|
return generated_value
|
246
318
|
|
@@ -254,63 +326,10 @@ class XmlGenerator:
|
|
254
326
|
|
255
327
|
raise RuntimeError(f"Can't generate value - unhandled type. Target name: {target_name}")
|
256
328
|
|
257
|
-
def
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
base_type = xsd_type.base_type
|
262
|
-
if not type_id:
|
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):
|
329
|
+
def _generate_boolean(self):
|
330
|
+
return self.randomizer.any(['true', 'false'])
|
331
|
+
|
332
|
+
def _generate_string(self, min_length, max_length, patterns):
|
314
333
|
if patterns is not None:
|
315
334
|
# Генерация строки по regex
|
316
335
|
random_enum = self.randomizer.any(patterns)
|
@@ -320,17 +339,14 @@ class XmlGenerator:
|
|
320
339
|
# Иначе генерируем случайную строку
|
321
340
|
return self.randomizer.ascii_string(min_length, max_length)
|
322
341
|
|
323
|
-
def
|
324
|
-
return self.randomizer.any(['true', 'false'])
|
325
|
-
|
326
|
-
def _generate_integer(self, total_digits, min_value, max_value):
|
342
|
+
def _generate_integer(self, min_value, max_value, total_digits):
|
327
343
|
if total_digits:
|
328
344
|
min_value = 10 ** (total_digits - 1)
|
329
345
|
max_value = (10 ** total_digits) - 1
|
330
346
|
rnd_int = self.randomizer.integer(min_value, max_value)
|
331
347
|
return str(rnd_int)
|
332
348
|
|
333
|
-
def _generate_decimal(self,
|
349
|
+
def _generate_decimal(self, rand_config, schema_min, schema_max, total_digits, fraction_digits):
|
334
350
|
if fraction_digits is None:
|
335
351
|
fraction_digits = self.randomizer.integer(1, 3)
|
336
352
|
|
@@ -345,22 +361,27 @@ class XmlGenerator:
|
|
345
361
|
|
346
362
|
integer_digits = total_digits - fraction_digits
|
347
363
|
|
348
|
-
# negative
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
364
|
+
# negative bound
|
365
|
+
digit_min = -(10 ** integer_digits - 1)
|
366
|
+
# positive bound
|
367
|
+
digit_max = 10 ** integer_digits - 1
|
368
|
+
logger.debug("integer digits: %s; digit_min: %s; digit_max: %s", integer_digits, digit_min, digit_max)
|
353
369
|
|
354
|
-
|
370
|
+
logger.debug('bounds before adjust: min_value: %4s; max_value: %4s', schema_min, schema_max)
|
371
|
+
config_min = rand_config.min_inclusive
|
372
|
+
config_max = rand_config.max_inclusive
|
373
|
+
effective_min, effective_max \
|
374
|
+
= merge_constraints(digit_min, digit_max, schema_min, schema_max, config_min, config_max)
|
375
|
+
logger.debug('bounds after adjust: min_value: %4s; max_value: %4s', effective_min, effective_max)
|
355
376
|
|
356
|
-
random_float = self.randomizer.float(
|
377
|
+
random_float = self.randomizer.float(effective_min, effective_max)
|
357
378
|
return f"{random_float:.{fraction_digits}f}"
|
358
379
|
|
359
|
-
def _generate_float(self, min_value, max_value):
|
360
|
-
return self.
|
380
|
+
def _generate_float(self, rand_config, min_value, max_value):
|
381
|
+
return self._generate_decimal(rand_config, min_value, max_value, None, 2)
|
361
382
|
|
362
|
-
def _generate_double(self, min_value, max_value):
|
363
|
-
return self._generate_decimal(
|
383
|
+
def _generate_double(self, rand_config, min_value, max_value):
|
384
|
+
return self._generate_decimal(rand_config, min_value, max_value, None, 2)
|
364
385
|
|
365
386
|
def _generate_duration(self):
|
366
387
|
raise RuntimeError("not yet implemented")
|
@@ -419,38 +440,41 @@ class XmlGenerator:
|
|
419
440
|
raise RuntimeError("not yet implemented")
|
420
441
|
|
421
442
|
|
422
|
-
def
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
if fact_max and new_min <= fact_max:
|
429
|
-
fact_min = new_min
|
430
|
-
|
431
|
-
if config_max:
|
432
|
-
if fact_max is None:
|
433
|
-
fact_max = config_max
|
434
|
-
else:
|
435
|
-
new_max = min(fact_max, config_max)
|
436
|
-
if new_max >= fact_min:
|
437
|
-
fact_max = new_max
|
438
|
-
|
439
|
-
if fact_max and fact_min and fact_max < fact_min:
|
440
|
-
fact_max = fact_min = min(fact_max, fact_min)
|
443
|
+
def merge_constraints(digit_min=None, digit_max=None, schema_min=None, schema_max=None, config_min=None,
|
444
|
+
config_max=None):
|
445
|
+
logger.debug(
|
446
|
+
"merge numeric constraints: "
|
447
|
+
"digit_min: %s, digit_max: %s, schema_min: %s, schema_max: %s, config_min: %s, config_max: %s",
|
448
|
+
digit_min, digit_max, schema_min, schema_max, config_min, config_max)
|
441
449
|
|
442
|
-
|
450
|
+
# За основу берем цифровые ограничения (они самые нестрогие)
|
451
|
+
effective_min, effective_max = digit_min, digit_max
|
443
452
|
|
453
|
+
# Применяем схемные ограничения
|
454
|
+
if schema_min is not None:
|
455
|
+
effective_min = max(effective_min, schema_min) if effective_min is not None else schema_min
|
456
|
+
if schema_max is not None:
|
457
|
+
effective_max = min(effective_max, schema_max) if effective_max is not None else schema_max
|
444
458
|
|
445
|
-
|
459
|
+
# Применяем конфигурационные ограничения с проверкой на конфликт
|
446
460
|
if config_min is not None:
|
447
|
-
|
448
|
-
|
449
|
-
|
461
|
+
if effective_max is not None and config_min > effective_max:
|
462
|
+
logger.warning("can't apply bound from configuration: config_min (%s) > effective_max (%s)",
|
463
|
+
config_min, effective_max)
|
464
|
+
else:
|
465
|
+
effective_min = max(effective_min, config_min) if effective_min is not None else config_min
|
450
466
|
|
451
467
|
if config_max is not None:
|
452
|
-
|
453
|
-
|
454
|
-
|
468
|
+
if effective_min is not None and config_max < effective_min:
|
469
|
+
logger.warning("can't apply bound from configuration: config_max (%s) < effective_min (%s)",
|
470
|
+
config_max, effective_min)
|
471
|
+
else:
|
472
|
+
effective_max = min(effective_max, config_max) if effective_max is not None else config_max
|
473
|
+
|
474
|
+
# Проверяем на конфликт
|
475
|
+
if effective_min is not None and effective_max is not None and effective_min > effective_max:
|
476
|
+
logger.warning("constrains conflict: effective_min (%s) > effective_max (%s). Swap values.",
|
477
|
+
effective_min, effective_max)
|
478
|
+
effective_min, effective_max = effective_max, effective_min
|
455
479
|
|
456
|
-
return
|
480
|
+
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):
|
xmlgenerator/substitution.py
CHANGED
@@ -61,7 +61,7 @@ class Substitutor:
|
|
61
61
|
resolved_value = self._process_expression(output_filename)
|
62
62
|
self._local_context['output_filename'] = resolved_value
|
63
63
|
|
64
|
-
logger.debug('
|
64
|
+
logger.debug('reset local context...')
|
65
65
|
logger.debug('local_context["source_filename"] = %s', xsd_filename)
|
66
66
|
logger.debug('local_context["source_extracted"] = %s (extracted with regexp %s)', source_extracted, source_filename)
|
67
67
|
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.4.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
|
@@ -0,0 +1,14 @@
|
|
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=T_Xy5PElb75EuyKIwXUGkQ2mntt3v2RwC1ulFI-CZnM,3654
|
4
|
+
xmlgenerator/configuration.py,sha256=JYhz_lONxd0faUiZHG-TVEs6yocn0s__Ulwtcvq9eDs,5946
|
5
|
+
xmlgenerator/generator.py,sha256=0hVlvod26PhR3-O2zEi-Tzdq4uCU13Xjeco5Srte5tQ,23228
|
6
|
+
xmlgenerator/randomization.py,sha256=OXh8ZmOKEU6AieGDl67d19i9MhobJKShONT19gDOFyo,4009
|
7
|
+
xmlgenerator/substitution.py,sha256=gaNcVqdDt3vbcBR8Pxf1mxo_qp8tTcd09VvVKnwlIA0,6019
|
8
|
+
xmlgenerator/validation.py,sha256=uCJjS5YmRDlAp9C-5Rd4E2Brh6_3WOG2-dSGxDiaH14,2023
|
9
|
+
xmlgenerator-0.4.0.dist-info/licenses/LICENSE,sha256=QlXK8O3UcoAYUYwVJNgB9MSM7O94ogNo_1hd9GzznUQ,1070
|
10
|
+
xmlgenerator-0.4.0.dist-info/METADATA,sha256=0Hi7lAhUvXxo7nXcOcxIFZ1ufmxFli5Hn4ROEt1eZHU,12935
|
11
|
+
xmlgenerator-0.4.0.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
|
12
|
+
xmlgenerator-0.4.0.dist-info/entry_points.txt,sha256=ly9hKr3o4AzFUkelBZNRzyKYf-Ld4kfcffvBu1oHq54,61
|
13
|
+
xmlgenerator-0.4.0.dist-info/top_level.txt,sha256=jr7FbMBm8MQ6j8I_-nWzQQEseXzwSCZNXgrkWuk9P4E,13
|
14
|
+
xmlgenerator-0.4.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
|