1 # Copyright (c) 2013, Schneider Electric Buildings AB
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are met:
6 # * Redistributions of source code must retain the above copyright
7 # notice, this list of conditions and the following disclaimer.
8 # * Redistributions in binary form must reproduce the above copyright
9 # notice, this list of conditions and the following disclaimer in the
10 # documentation and/or other materials provided with the distribution.
11 # * Neither the name of Schneider Electric Buildings AB nor the
12 # names of contributors may be used to endorse or promote products
13 # derived from this software without specific prior written permission.
15 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
19 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 from __future__ import print_function # Python 2 compatibility
30 from asn1ate import parser
31 from asn1ate.support import pygen
32 from asn1ate.sema import *
35 class Pyasn1Backend(object):
36 """ Backend to generate pyasn1 declarations from semantic tree.
38 Pyasn1 represents type assignments as class derivation, e.g.
41 class Foo(univ.Integer):
44 For constructed types, the component types are instantiated inline, e.g.
49 class Seq(univ.Sequence):
50 componentType = namedtype.NamedTypes(
51 namedtype.NamedType('foo', univ.Integer())
54 (univ.Integer is not a base class here, but a value.)
56 To cope with circular dependencies, we define types in two passes so we'll
57 generate the above as:
59 class Seq(univ.Sequence):
62 Seq.componentType = namedtype.NamedTypes(
63 namedtype.NamedType('foo', univ.Integer())
66 This is nice, because we separate the introduction of a name (``Seq``) from
67 the details of what it contains, so we can build recursive definitions
68 without getting into trouble with Python's name lookup.
70 We call the empty class a *declaration*, and the population of its members
71 *definition*. The instantiation of univ.Integer is called an
74 The translation from ASN.1 constructs to Pyasn1 come in different flavors,
75 depending on whether they're declarations, definitions or inline
78 Only type and value assignments generate declarations. For type assignments
79 we generate a definition once all dependent declarations are created. If the
80 type assignment involves a constructed type, it is filled with inline
83 def __init__(self, sema_module, out_stream):
84 self.sema_module = sema_module
85 self.writer = pygen.PythonWriter(out_stream)
87 self.decl_generators = {
88 TypeAssignment: self.decl_type_assignment,
89 ValueAssignment: self.decl_value_assignment
92 self.defn_generators = {
93 ChoiceType: self.defn_constructed_type,
94 SequenceType: self.defn_constructed_type,
95 SetType: self.defn_constructed_type,
96 SequenceOfType: self.defn_collection_type,
97 SetOfType: self.defn_collection_type,
98 TaggedType: self.defn_tagged_type,
99 SelectionType: self.defn_selection_type,
100 SimpleType: self.defn_simple_type,
101 DefinedType: self.defn_defined_type,
102 ValueListType: self.defn_value_list_type,
103 BitStringType: self.defn_bitstring_type,
106 self.inline_generators = {
107 TaggedType: self.inline_tagged_type,
108 SelectionType: self.inline_selection_type,
109 SimpleType: self.inline_simple_type,
110 DefinedType: self.inline_defined_type,
111 ComponentType: self.inline_component_type,
112 NamedType: self.inline_named_type,
113 SequenceOfType: self.inline_sequenceof_type,
114 SetOfType: self.inline_setof_type,
115 ValueListType: self.inline_value_list_type,
116 ChoiceType: self.inline_constructed_type,
117 SequenceType: self.inline_constructed_type,
118 SetType: self.inline_constructed_type,
121 def generate_code(self):
122 self.writer.write_line('from pyasn1.type import univ, char, namedtype, namedval, tag, constraint, useful')
123 self.writer.write_blanks(2)
125 # Generate _OID if sema_module contains any object identifier values.
126 oids = [n for n in self.sema_module.descendants() if isinstance(n, ObjectIdentifierValue)]
128 self.writer.write_block(self.generate_OID())
129 self.writer.write_blanks(2)
131 assignment_components = dependency_sort(self.sema_module.assignments)
132 for component in assignment_components:
133 for assignment in component:
134 self.writer.write_block(self.generate_decl(assignment))
135 self.writer.write_blanks(2)
137 for assignment in component:
138 details = self.generate_definition(assignment)
140 self.writer.write_block(details)
141 self.writer.write_blanks(2)
143 def generate_definition(self, assignment):
144 assert isinstance(assignment, (ValueAssignment, TypeAssignment))
146 if isinstance(assignment, ValueAssignment):
147 return None # Nothing to do here.
149 assigned_type, type_decl = assignment.type_name, assignment.type_decl
150 assigned_type = _translate_type(assigned_type)
151 return self.generate_defn(assigned_type, type_decl)
153 def generate_decl(self, t):
154 generator = self.decl_generators[type(t)]
157 def generate_expr(self, t):
158 generator = self.inline_generators[type(t)]
161 def generate_defn(self, class_name, t):
162 generator = self.defn_generators[type(t)]
163 return generator(class_name, t)
165 def decl_type_assignment(self, assignment):
166 fragment = self.writer.get_fragment()
168 assigned_type, type_decl = assignment.type_name, assignment.type_decl
170 if isinstance(type_decl, SelectionType):
171 type_decl = self.sema_module.resolve_selection_type(type_decl)
173 assigned_type = _translate_type(assigned_type)
174 base_type = _translate_type(type_decl.type_name)
175 fragment.write_line('class %s(%s):' % (assigned_type, base_type))
176 fragment.push_indent()
177 fragment.write_line('pass')
178 fragment.pop_indent()
182 def decl_value_assignment(self, assignment):
183 assigned_value, type_decl, value = assignment.value_name, assignment.type_decl, assignment.value
184 assigned_value = _sanitize_identifier(assigned_value)
185 construct_expr = self.build_value_construct_expr(type_decl, value)
186 return '%s = %s' % (assigned_value, construct_expr)
188 def defn_simple_type(self, class_name, t):
190 return '%s.subtypeSpec = %s' % (class_name, self.build_constraint_expr(t.constraint))
194 def defn_defined_type(self, class_name, t):
197 def defn_constructed_type(self, class_name, t):
198 fragment = self.writer.get_fragment()
200 fragment.write_line('%s.componentType = namedtype.NamedTypes(' % class_name)
201 fragment.push_indent()
202 fragment.write_block(self.inline_component_types(t.components))
203 fragment.pop_indent()
204 fragment.write_line(')')
208 def defn_tagged_type(self, class_name, t):
209 fragment = self.writer.get_fragment()
211 tag_type = 'tagImplicitly' if t.implicit else 'tagExplicitly'
212 base_type = _translate_type(t.type_decl.type_name)
214 fragment.write_line('%s.tagSet = %s.tagSet.%s(%s)' % (class_name, base_type, tag_type, self.build_tag_expr(t)))
215 nested_dfn = self.generate_defn(class_name, t.type_decl)
217 fragment.write_line(nested_dfn)
221 def defn_selection_type(self, class_name, t):
224 def defn_value_list_type(self, class_name, t):
225 fragment = self.writer.get_fragment()
228 fragment.write_line('%s.namedValues = namedval.NamedValues(' % class_name)
229 fragment.push_indent()
231 named_values = ['(\'%s\', %s)' % (v.identifier, v.value) for v in t.named_values if not isinstance(v, ExtensionMarker)]
232 fragment.write_enumeration(named_values)
234 fragment.pop_indent()
235 fragment.write_line(')')
238 fragment.write_line('%s.subtypeSpec=%s' % (class_name, self.build_constraint_expr(t.constraint)))
242 def defn_bitstring_type(self, class_name, t):
243 fragment = self.writer.get_fragment()
246 fragment.write_line('%s.namedValues = namedval.NamedValues(' % class_name)
247 fragment.push_indent()
248 named_bits = ['(\'%s\', %s)' % (b.identifier, b.value) for b in t.named_bits]
249 fragment.write_enumeration(named_bits)
250 fragment.pop_indent()
251 fragment.write_line(')')
254 fragment.write_line('%s.subtypeSpec=%s' % (class_name, self.build_constraint_expr(t.constraint)))
258 def defn_collection_type(self, class_name, t):
259 fragment = self.writer.get_fragment()
260 fragment.write_line('%s.componentType = %s' % (class_name, self.generate_expr(t.type_decl)))
262 if t.size_constraint:
263 fragment.write_line('%s.subtypeSpec=%s' % (class_name, self.build_constraint_expr(t.size_constraint)))
267 def inline_simple_type(self, t):
268 type_expr = _translate_type(t.type_name) + '()'
270 type_expr += '.subtype(subtypeSpec=%s)' % self.build_constraint_expr(t.constraint)
274 def inline_defined_type(self, t):
275 return _translate_type(t.type_name) + '()'
277 def inline_constructed_type(self, t):
278 fragment = self.writer.get_fragment()
280 class_name = _translate_type(t.type_name)
282 fragment.write_line('%s(componentType=namedtype.NamedTypes(' % class_name)
284 fragment.push_indent()
285 fragment.write_block(self.inline_component_types(t.components))
286 fragment.pop_indent()
288 fragment.write_line('))')
292 def inline_component_types(self, components):
293 fragment = self.writer.get_fragment()
297 if not isinstance(c, ExtensionMarker):
298 component_exprs.append(self.generate_expr(c))
300 fragment.write_enumeration(component_exprs)
304 def inline_tagged_type(self, t):
305 tag_type = 'implicitTag' if t.implicit else 'explicitTag'
306 type_expr = self.generate_expr(t.type_decl)
307 type_expr += '.subtype(%s=%s)' % (tag_type, self.build_tag_expr(t))
311 def inline_selection_type(self, t):
312 selected_type = self.sema_module.resolve_selection_type(t)
313 assert selected_type is not None, "Found no member %s in %s" % (t.identifier, t.type_decl)
315 return self.generate_expr(selected_type)
317 def build_tag_expr(self, tag_def):
318 context = _translate_tag_class(tag_def.class_name)
320 tagged_type_decl = self.sema_module.resolve_type_decl(tag_def.type_decl)
321 if isinstance(tagged_type_decl, ConstructedType):
322 tag_format = 'tag.tagFormatConstructed'
324 tag_format = 'tag.tagFormatSimple'
326 return 'tag.Tag(%s, %s, %s)' % (context, tag_format, tag_def.class_number)
328 def build_constraint_expr(self, constraint):
329 def unpack_size_constraint(nested):
330 if isinstance(nested, SingleValueConstraint):
331 return _translate_value(nested.value), _translate_value(nested.value)
332 elif isinstance(nested, ValueRangeConstraint):
333 return _translate_value(nested.min_value), _translate_value(nested.max_value)
335 assert False, "Unrecognized nested size constraint type: %s" % type(constraint.nested).__name__
337 if isinstance(constraint, SingleValueConstraint):
338 return 'constraint.SingleValueConstraint(%s)' % (_translate_value(constraint.value))
339 elif isinstance(constraint, SizeConstraint):
340 min_value, max_value = unpack_size_constraint(constraint.nested)
341 return 'constraint.ValueSizeConstraint(%s, %s)' % (_translate_value(min_value), _translate_value(max_value))
342 elif isinstance (constraint, ValueRangeConstraint):
343 return 'constraint.ValueRangeConstraint(%s, %s)' % (_translate_value(constraint.min_value),
344 _translate_value(constraint.max_value))
346 assert False, "Unrecognized constraint type: %s" % type(constraint).__name__
348 def build_value_construct_expr(self, type_decl, value):
349 """ Build a valid construct-expression for values, depending on
350 the target pyasn1 type.
353 def build_value_expr(type_name, value):
354 """ Special treatment for bstring and hstring values,
355 which use different construction depending on target type.
357 if isinstance(value, BinaryStringValue):
358 if type_name == 'OCTET STRING':
359 return 'binValue=\'%s\'' % value.value
361 return '"\'%s\'B"' % value.value
362 elif isinstance(value, HexStringValue):
363 if type_name == 'OCTET STRING':
364 return 'hexValue=\'%s\'' % value.value
366 return '"\'%s\'H"' % value.value
368 return _translate_value(value)
370 if isinstance(value, ObjectIdentifierValue):
371 return self.build_object_identifier_value(value)
373 value_type = _translate_type(type_decl.type_name)
374 root_type = self.sema_module.resolve_type_decl(type_decl)
375 return '%s(%s)' % (value_type, build_value_expr(root_type.type_name, value))
377 def inline_component_type(self, t):
378 if t.components_of_type:
379 # COMPONENTS OF works like a literal include, so just
380 # expand all components of the referenced type.
381 included_type_decl = self.sema_module.resolve_type_decl(t.components_of_type)
382 included_content = self.inline_component_types(included_type_decl.components)
384 # Strip trailing newline from inline_component_types
385 # to make the list line up
386 return included_content.strip()
389 return "namedtype.OptionalNamedType('%s', %s)" % (t.identifier, self.generate_expr(t.type_decl))
390 elif t.default_value is not None:
391 type_expr = self.generate_expr(t.type_decl)
392 type_expr += '.subtype(value=%s)' % _translate_value(t.default_value)
394 return "namedtype.DefaultedNamedType('%s', %s)" % (t.identifier, type_expr)
396 return "namedtype.NamedType('%s', %s)" % (t.identifier, self.generate_expr(t.type_decl))
398 def inline_named_type(self, t):
399 return "namedtype.NamedType('%s', %s)" % (t.identifier, self.generate_expr(t.type_decl))
401 def inline_value_list_type(self, t):
402 class_name = _translate_type(t.type_name)
404 named_values = ['(\'%s\', %s)' % (v.identifier, v.value) for v in t.named_values if not isinstance(v, ExtensionMarker)]
405 return '%s(namedValues=namedval.NamedValues(%s))' % (class_name, ', '.join(named_values))
407 return class_name + '()'
409 def inline_sequenceof_type(self, t):
410 return 'univ.SequenceOf(componentType=%s)' % self.generate_expr(t.type_decl)
412 def inline_setof_type(self, t):
413 return 'univ.SetOf(componentType=%s)' % self.generate_expr(t.type_decl)
415 def build_object_identifier_value(self, t):
416 objid_components = []
418 for c in t.components:
419 if isinstance(c, NameForm):
420 if c.name in REGISTERED_OID_NAMES:
421 objid_components.append(str(REGISTERED_OID_NAMES[c.name]))
423 objid_components.append(_translate_value(c.name))
424 elif isinstance(c, NumberForm):
425 objid_components.append(str(c.value))
426 elif isinstance(c, NameAndNumberForm):
427 objid_components.append(str(c.number.value))
431 return '_OID(%s)' % ', '.join(objid_components)
433 def generate_OID(self):
434 fragment = self.writer.get_fragment()
436 fragment.write_line('def _OID(*components):')
437 fragment.push_indent()
438 fragment.write_line('output = []')
439 fragment.write_line('for x in tuple(components):')
440 fragment.push_indent()
441 fragment.write_line('if isinstance(x, univ.ObjectIdentifier):')
442 fragment.push_indent()
443 fragment.write_line('output.extend(list(x))')
444 fragment.pop_indent()
445 fragment.write_line('else:')
446 fragment.push_indent()
447 fragment.write_line('output.append(int(x))')
448 fragment.pop_indent()
449 fragment.pop_indent()
450 fragment.write_blanks(1)
451 fragment.write_line('return univ.ObjectIdentifier(output)')
452 fragment.pop_indent()
454 fragment.pop_indent()
459 def generate_pyasn1(sema_module, out_stream):
460 return Pyasn1Backend(sema_module, out_stream).generate_code()
463 # Translation tables from ASN.1 primitives to pyasn1 primitives
464 _ASN1_TAG_CONTEXTS = {
465 'APPLICATION': 'tag.tagClassApplication',
466 'PRIVATE': 'tag.tagClassPrivate',
467 'UNIVERSAL': 'tag.tagClassUniversal'
471 _ASN1_BUILTIN_VALUES = {
477 _ASN1_BUILTIN_TYPES = {
479 'INTEGER': 'univ.Integer',
480 'BOOLEAN': 'univ.Boolean',
482 'ENUMERATED': 'univ.Enumerated',
484 'BIT STRING': 'univ.BitString',
485 'OCTET STRING': 'univ.OctetString',
486 'CHOICE': 'univ.Choice',
487 'SEQUENCE': 'univ.Sequence',
489 'SEQUENCE OF': 'univ.SequenceOf',
490 'SET OF': 'univ.SetOf',
491 'OBJECT IDENTIFIER': 'univ.ObjectIdentifier',
492 'UTF8String': 'char.UTF8String',
493 'GeneralString': 'char.GeneralString',
494 'NumericString': 'char.NumericString',
495 'PrintableString': 'char.PrintableString',
496 'IA5String': 'char.IA5String',
497 'GeneralizedTime': 'useful.GeneralizedTime',
498 'UTCTime': 'useful.UTCTime',
499 'ObjectDescriptor': 'useful.ObjectDescriptor', # In pyasn1 r1.2
503 def _translate_type(type_name):
504 """ Translate ASN.1 built-in types to pyasn1 equivalents.
505 Non-builtins are not translated.
507 assert isinstance(type_name, str), "Type name must be a string."
508 type_name = _sanitize_identifier(type_name)
510 return _ASN1_BUILTIN_TYPES.get(type_name, type_name)
513 def _translate_tag_class(tag_class):
514 """ Translate ASN.1 tag class names to pyasn1 equivalents.
515 Defaults to tag.tagClassContext if tag_class is not
518 return _ASN1_TAG_CONTEXTS.get(tag_class, 'tag.tagClassContext')
521 def _translate_value(value):
522 """ Translate ASN.1 built-in values to Python equivalents.
523 Unrecognized values are not translated.
525 if isinstance(value, ReferencedValue) or _heuristic_is_identifier(value):
526 value = _sanitize_identifier(value)
528 return _ASN1_BUILTIN_VALUES.get(value, value)
531 def _heuristic_is_identifier(value):
532 """ Return True if this value is likely an identifier.
534 first = str(value)[0]
535 return first != '-' and not first.isdigit()
538 def _sanitize_identifier(name):
539 """ Sanitize ASN.1 type and value identifiers so that they're
540 valid Python identifiers.
543 name = name.replace('-', '_')
544 if name in keyword.kwlist:
550 # Simplistic command-line driver
552 with open(args[0]) as f:
555 parse_tree = parser.parse_asn1(asn1def)
557 modules = build_semantic_model(parse_tree)
559 print('WARNING: More than one module generated to the same stream.', file=sys.stderr)
561 for module in modules:
562 print(pygen.auto_generated_header())
563 generate_pyasn1(module, sys.stdout)
568 if __name__ == '__main__':
569 sys.exit(main(sys.argv[1:]))