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,
119 BitStringType: self.inline_bitstring_type,
122 def generate_code(self):
123 self.writer.write_line('from pyasn1.type import univ, char, namedtype, namedval, tag, constraint, useful')
124 self.writer.write_blanks(2)
126 # Generate _OID if sema_module contains any object identifier values.
127 oids = [n for n in self.sema_module.descendants() if isinstance(n, ObjectIdentifierValue)]
129 self.writer.write_block(self.generate_OID())
130 self.writer.write_blanks(2)
132 assignment_components = dependency_sort(self.sema_module.assignments)
133 for component in assignment_components:
134 for assignment in component:
135 self.writer.write_block(self.generate_decl(assignment))
136 self.writer.write_blanks(2)
138 for assignment in component:
139 details = self.generate_definition(assignment)
141 self.writer.write_block(details)
142 self.writer.write_blanks(2)
144 def generate_definition(self, assignment):
145 assert isinstance(assignment, (ValueAssignment, TypeAssignment))
147 if isinstance(assignment, ValueAssignment):
148 return None # Nothing to do here.
150 assigned_type, type_decl = assignment.type_name, assignment.type_decl
151 assigned_type = _translate_type(assigned_type)
152 return self.generate_defn(assigned_type, type_decl)
154 def generate_decl(self, t):
155 generator = self.decl_generators[type(t)]
158 def generate_expr(self, t):
159 generator = self.inline_generators[type(t)]
162 def generate_defn(self, class_name, t):
163 generator = self.defn_generators[type(t)]
164 return generator(class_name, t)
166 def decl_type_assignment(self, assignment):
167 fragment = self.writer.get_fragment()
169 assigned_type, type_decl = assignment.type_name, assignment.type_decl
171 if isinstance(type_decl, SelectionType):
172 type_decl = self.sema_module.resolve_selection_type(type_decl)
174 assigned_type = _translate_type(assigned_type)
175 base_type = _translate_type(type_decl.type_name)
176 fragment.write_line('class %s(%s):' % (assigned_type, base_type))
177 fragment.push_indent()
178 fragment.write_line('pass')
179 fragment.pop_indent()
183 def decl_value_assignment(self, assignment):
184 assigned_value, type_decl, value = assignment.value_name, assignment.type_decl, assignment.value
185 assigned_value = _sanitize_identifier(assigned_value)
186 construct_expr = self.build_value_construct_expr(type_decl, value)
187 return '%s = %s' % (assigned_value, construct_expr)
189 def defn_simple_type(self, class_name, t):
191 return '%s.subtypeSpec = %s' % (class_name, self.build_constraint_expr(t.constraint))
195 def defn_defined_type(self, class_name, t):
198 def defn_constructed_type(self, class_name, t):
199 fragment = self.writer.get_fragment()
201 fragment.write_line('%s.componentType = namedtype.NamedTypes(' % class_name)
202 fragment.push_indent()
203 fragment.write_block(self.inline_component_types(t.components))
204 fragment.pop_indent()
205 fragment.write_line(')')
209 def defn_tagged_type(self, class_name, t):
210 fragment = self.writer.get_fragment()
212 implicity = self.sema_module.resolve_tag_implicity(t.implicity, t.type_decl)
213 if implicity == TagImplicity.IMPLICIT:
214 tag_implicity = 'tagImplicitly'
215 elif implicity == TagImplicity.EXPLICIT:
216 tag_implicity = 'tagExplicitly'
218 assert False, "Unexpected implicity: %s" % implicity
220 base_type = _translate_type(t.type_decl.type_name)
222 fragment.write_line('%s.tagSet = %s.tagSet.%s(%s)' % (class_name, base_type, tag_implicity, self.build_tag_expr(t)))
223 nested_dfn = self.generate_defn(class_name, t.type_decl)
225 fragment.write_line(nested_dfn)
229 def defn_selection_type(self, class_name, t):
232 def defn_value_list_type(self, class_name, t):
233 fragment = self.writer.get_fragment()
236 fragment.write_line('%s.namedValues = namedval.NamedValues(' % class_name)
237 fragment.push_indent()
239 named_values = ['(\'%s\', %s)' % (v.identifier, v.value) for v in t.named_values if not isinstance(v, ExtensionMarker)]
240 fragment.write_enumeration(named_values)
242 fragment.pop_indent()
243 fragment.write_line(')')
246 fragment.write_line('%s.subtypeSpec=%s' % (class_name, self.build_constraint_expr(t.constraint)))
250 def inline_bitstring_type(self, t):
251 return self.inline_simple_type(t)
253 def defn_bitstring_type(self, class_name, t):
254 fragment = self.writer.get_fragment()
257 fragment.write_line('%s.namedValues = namedval.NamedValues(' % class_name)
258 fragment.push_indent()
259 named_bits = ['(\'%s\', %s)' % (b.identifier, b.value) for b in t.named_bits]
260 fragment.write_enumeration(named_bits)
261 fragment.pop_indent()
262 fragment.write_line(')')
265 fragment.write_line('%s.subtypeSpec=%s' % (class_name, self.build_constraint_expr(t.constraint)))
269 def defn_collection_type(self, class_name, t):
270 fragment = self.writer.get_fragment()
271 fragment.write_line('%s.componentType = %s' % (class_name, self.generate_expr(t.type_decl)))
273 if t.size_constraint:
274 fragment.write_line('%s.subtypeSpec=%s' % (class_name, self.build_constraint_expr(t.size_constraint)))
278 def inline_simple_type(self, t):
279 type_expr = _translate_type(t.type_name) + '()'
281 type_expr += '.subtype(subtypeSpec=%s)' % self.build_constraint_expr(t.constraint)
285 def inline_defined_type(self, t):
286 return _translate_type(t.type_name) + '()'
288 def inline_constructed_type(self, t):
289 fragment = self.writer.get_fragment()
291 class_name = _translate_type(t.type_name)
293 fragment.write_line('%s(componentType=namedtype.NamedTypes(' % class_name)
295 fragment.push_indent()
296 fragment.write_block(self.inline_component_types(t.components))
297 fragment.pop_indent()
299 fragment.write_line('))')
303 def inline_component_types(self, components):
304 fragment = self.writer.get_fragment()
308 if not isinstance(c, ExtensionMarker):
309 component_exprs.append(self.generate_expr(c))
311 fragment.write_enumeration(component_exprs)
315 def inline_tagged_type(self, t):
316 implicity = self.sema_module.resolve_tag_implicity(t.implicity, t.type_decl)
317 if implicity == TagImplicity.IMPLICIT:
318 tag_implicity = 'implicitTag'
319 elif implicity == TagImplicity.EXPLICIT:
320 tag_implicity = 'explicitTag'
322 assert False, "Unexpected implicity: %s" % implicity
324 type_expr = self.generate_expr(t.type_decl)
325 type_expr += '.subtype(%s=%s)' % (tag_implicity, self.build_tag_expr(t))
329 def inline_selection_type(self, t):
330 selected_type = self.sema_module.resolve_selection_type(t)
331 assert selected_type is not None, "Found no member %s in %s" % (t.identifier, t.type_decl)
333 return self.generate_expr(selected_type)
335 def build_tag_expr(self, tag_def):
336 context = _translate_tag_class(tag_def.class_name)
338 tagged_type_decl = self.sema_module.resolve_type_decl(tag_def.type_decl)
339 if isinstance(tagged_type_decl, ConstructedType):
340 tag_format = 'tag.tagFormatConstructed'
342 tag_format = 'tag.tagFormatSimple'
344 return 'tag.Tag(%s, %s, %s)' % (context, tag_format, tag_def.class_number)
346 def build_constraint_expr(self, constraint):
347 def unpack_size_constraint(nested):
348 if isinstance(nested, SingleValueConstraint):
349 return _translate_value(nested.value), _translate_value(nested.value)
350 elif isinstance(nested, ValueRangeConstraint):
351 return _translate_value(nested.min_value), _translate_value(nested.max_value)
353 assert False, "Unrecognized nested size constraint type: %s" % type(constraint.nested).__name__
355 if isinstance(constraint, SingleValueConstraint):
356 return 'constraint.SingleValueConstraint(%s)' % (_translate_value(constraint.value))
357 elif isinstance(constraint, SizeConstraint):
358 min_value, max_value = unpack_size_constraint(constraint.nested)
359 return 'constraint.ValueSizeConstraint(%s, %s)' % (_translate_value(min_value), _translate_value(max_value))
360 elif isinstance (constraint, ValueRangeConstraint):
361 return 'constraint.ValueRangeConstraint(%s, %s)' % (_translate_value(constraint.min_value),
362 _translate_value(constraint.max_value))
364 assert False, "Unrecognized constraint type: %s" % type(constraint).__name__
366 def build_value_construct_expr(self, type_decl, value):
367 """ Build a valid construct-expression for values, depending on
368 the target pyasn1 type.
371 def build_value_expr(type_name, value):
372 """ Special treatment for bstring and hstring values,
373 which use different construction depending on target type.
375 if isinstance(value, BinaryStringValue):
376 if type_name == 'OCTET STRING':
377 return 'binValue=\'%s\'' % value.value
379 return '"\'%s\'B"' % value.value
380 elif isinstance(value, HexStringValue):
381 if type_name == 'OCTET STRING':
382 return 'hexValue=\'%s\'' % value.value
384 return '"\'%s\'H"' % value.value
386 return _translate_value(value)
388 if isinstance(value, ObjectIdentifierValue):
389 return self.build_object_identifier_value(value)
391 value_type = _translate_type(type_decl.type_name)
392 root_type = self.sema_module.resolve_type_decl(type_decl)
393 return '%s(%s)' % (value_type, build_value_expr(root_type.type_name, value))
395 def inline_component_type(self, t):
396 if t.components_of_type:
397 # COMPONENTS OF works like a literal include, so just
398 # expand all components of the referenced type.
399 included_type_decl = self.sema_module.resolve_type_decl(t.components_of_type)
400 included_content = self.inline_component_types(included_type_decl.components)
402 # Strip trailing newline from inline_component_types
403 # to make the list line up
404 return included_content.strip()
407 return "namedtype.OptionalNamedType('%s', %s)" % (t.identifier, self.generate_expr(t.type_decl))
408 elif t.default_value is not None:
409 type_expr = self.generate_expr(t.type_decl)
410 type_expr += '.subtype(value=%s)' % _translate_value(t.default_value)
412 return "namedtype.DefaultedNamedType('%s', %s)" % (t.identifier, type_expr)
414 return "namedtype.NamedType('%s', %s)" % (t.identifier, self.generate_expr(t.type_decl))
416 def inline_named_type(self, t):
417 return "namedtype.NamedType('%s', %s)" % (t.identifier, self.generate_expr(t.type_decl))
419 def inline_value_list_type(self, t):
420 class_name = _translate_type(t.type_name)
422 named_values = ['(\'%s\', %s)' % (v.identifier, v.value) for v in t.named_values if not isinstance(v, ExtensionMarker)]
423 return '%s(namedValues=namedval.NamedValues(%s))' % (class_name, ', '.join(named_values))
425 return class_name + '()'
427 def inline_sequenceof_type(self, t):
428 return 'univ.SequenceOf(componentType=%s)' % self.generate_expr(t.type_decl)
430 def inline_setof_type(self, t):
431 return 'univ.SetOf(componentType=%s)' % self.generate_expr(t.type_decl)
433 def build_object_identifier_value(self, t):
434 objid_components = []
436 for c in t.components:
437 if isinstance(c, NameForm):
438 if c.name in REGISTERED_OID_NAMES:
439 objid_components.append(str(REGISTERED_OID_NAMES[c.name]))
441 objid_components.append(_translate_value(c.name))
442 elif isinstance(c, NumberForm):
443 objid_components.append(str(c.value))
444 elif isinstance(c, NameAndNumberForm):
445 objid_components.append(str(c.number.value))
449 return '_OID(%s)' % ', '.join(objid_components)
451 def generate_OID(self):
452 fragment = self.writer.get_fragment()
454 fragment.write_line('def _OID(*components):')
455 fragment.push_indent()
456 fragment.write_line('output = []')
457 fragment.write_line('for x in tuple(components):')
458 fragment.push_indent()
459 fragment.write_line('if isinstance(x, univ.ObjectIdentifier):')
460 fragment.push_indent()
461 fragment.write_line('output.extend(list(x))')
462 fragment.pop_indent()
463 fragment.write_line('else:')
464 fragment.push_indent()
465 fragment.write_line('output.append(int(x))')
466 fragment.pop_indent()
467 fragment.pop_indent()
468 fragment.write_blanks(1)
469 fragment.write_line('return univ.ObjectIdentifier(output)')
470 fragment.pop_indent()
472 fragment.pop_indent()
477 def generate_pyasn1(sema_module, out_stream):
478 return Pyasn1Backend(sema_module, out_stream).generate_code()
481 # Translation tables from ASN.1 primitives to pyasn1 primitives
482 _ASN1_TAG_CONTEXTS = {
483 'APPLICATION': 'tag.tagClassApplication',
484 'PRIVATE': 'tag.tagClassPrivate',
485 'UNIVERSAL': 'tag.tagClassUniversal'
489 _ASN1_BUILTIN_VALUES = {
495 _ASN1_BUILTIN_TYPES = {
497 'INTEGER': 'univ.Integer',
498 'BOOLEAN': 'univ.Boolean',
500 'ENUMERATED': 'univ.Enumerated',
502 'BIT STRING': 'univ.BitString',
503 'OCTET STRING': 'univ.OctetString',
504 'CHOICE': 'univ.Choice',
505 'SEQUENCE': 'univ.Sequence',
507 'SEQUENCE OF': 'univ.SequenceOf',
508 'SET OF': 'univ.SetOf',
509 'OBJECT IDENTIFIER': 'univ.ObjectIdentifier',
510 'UTF8String': 'char.UTF8String',
511 'GeneralString': 'char.GeneralString',
512 'NumericString': 'char.NumericString',
513 'PrintableString': 'char.PrintableString',
514 'IA5String': 'char.IA5String',
515 'GraphicString': 'char.GraphicString',
516 'GeneralizedTime': 'useful.GeneralizedTime',
517 'UTCTime': 'useful.UTCTime',
518 'ObjectDescriptor': 'useful.ObjectDescriptor', # In pyasn1 r1.2
522 def _translate_type(type_name):
523 """ Translate ASN.1 built-in types to pyasn1 equivalents.
524 Non-builtins are not translated.
526 assert isinstance(type_name, str), "Type name must be a string."
527 type_name = _sanitize_identifier(type_name)
529 return _ASN1_BUILTIN_TYPES.get(type_name, type_name)
532 def _translate_tag_class(tag_class):
533 """ Translate ASN.1 tag class names to pyasn1 equivalents.
534 Defaults to tag.tagClassContext if tag_class is not
537 return _ASN1_TAG_CONTEXTS.get(tag_class, 'tag.tagClassContext')
540 def _translate_value(value):
541 """ Translate ASN.1 built-in values to Python equivalents.
542 Unrecognized values are not translated.
544 if isinstance(value, ReferencedValue) or _heuristic_is_identifier(value):
545 value = _sanitize_identifier(value)
547 return _ASN1_BUILTIN_VALUES.get(value, value)
550 def _heuristic_is_identifier(value):
551 """ Return True if this value is likely an identifier.
553 first = str(value)[0]
554 return first != '-' and not first.isdigit()
557 def _sanitize_identifier(name):
558 """ Sanitize ASN.1 type and value identifiers so that they're
559 valid Python identifiers.
562 name = name.replace('-', '_')
563 if name in keyword.kwlist:
569 # Simplistic command-line driver
571 with open(args[0]) as f:
574 parse_tree = parser.parse_asn1(asn1def)
576 modules = build_semantic_model(parse_tree)
578 print('WARNING: More than one module generated to the same stream.', file=sys.stderr)
580 for module in modules:
581 print(pygen.auto_generated_header())
582 generate_pyasn1(module, sys.stdout)
587 if __name__ == '__main__':
588 sys.exit(main(sys.argv[1:]))