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 implicity = self.sema_module.resolve_tag_implicity(t.implicity, t.type_decl)
212 if implicity == TagImplicity.IMPLICIT:
213 tag_implicity = 'tagImplicitly'
214 elif implicity == TagImplicity.EXPLICIT:
215 tag_implicity = 'tagExplicitly'
217 assert False, "Unexpected implicity: %s" % implicity
219 base_type = _translate_type(t.type_decl.type_name)
221 fragment.write_line('%s.tagSet = %s.tagSet.%s(%s)' % (class_name, base_type, tag_implicity, self.build_tag_expr(t)))
222 nested_dfn = self.generate_defn(class_name, t.type_decl)
224 fragment.write_line(nested_dfn)
228 def defn_selection_type(self, class_name, t):
231 def defn_value_list_type(self, class_name, t):
232 fragment = self.writer.get_fragment()
235 fragment.write_line('%s.namedValues = namedval.NamedValues(' % class_name)
236 fragment.push_indent()
238 named_values = ['(\'%s\', %s)' % (v.identifier, v.value) for v in t.named_values if not isinstance(v, ExtensionMarker)]
239 fragment.write_enumeration(named_values)
241 fragment.pop_indent()
242 fragment.write_line(')')
245 fragment.write_line('%s.subtypeSpec=%s' % (class_name, self.build_constraint_expr(t.constraint)))
249 def defn_bitstring_type(self, class_name, t):
250 fragment = self.writer.get_fragment()
253 fragment.write_line('%s.namedValues = namedval.NamedValues(' % class_name)
254 fragment.push_indent()
255 named_bits = ['(\'%s\', %s)' % (b.identifier, b.value) for b in t.named_bits]
256 fragment.write_enumeration(named_bits)
257 fragment.pop_indent()
258 fragment.write_line(')')
261 fragment.write_line('%s.subtypeSpec=%s' % (class_name, self.build_constraint_expr(t.constraint)))
265 def defn_collection_type(self, class_name, t):
266 fragment = self.writer.get_fragment()
267 fragment.write_line('%s.componentType = %s' % (class_name, self.generate_expr(t.type_decl)))
269 if t.size_constraint:
270 fragment.write_line('%s.subtypeSpec=%s' % (class_name, self.build_constraint_expr(t.size_constraint)))
274 def inline_simple_type(self, t):
275 type_expr = _translate_type(t.type_name) + '()'
277 type_expr += '.subtype(subtypeSpec=%s)' % self.build_constraint_expr(t.constraint)
281 def inline_defined_type(self, t):
282 return _translate_type(t.type_name) + '()'
284 def inline_constructed_type(self, t):
285 fragment = self.writer.get_fragment()
287 class_name = _translate_type(t.type_name)
289 fragment.write_line('%s(componentType=namedtype.NamedTypes(' % class_name)
291 fragment.push_indent()
292 fragment.write_block(self.inline_component_types(t.components))
293 fragment.pop_indent()
295 fragment.write_line('))')
299 def inline_component_types(self, components):
300 fragment = self.writer.get_fragment()
304 if not isinstance(c, ExtensionMarker):
305 component_exprs.append(self.generate_expr(c))
307 fragment.write_enumeration(component_exprs)
311 def inline_tagged_type(self, t):
312 implicity = self.sema_module.resolve_tag_implicity(t.implicity, t.type_decl)
313 if implicity == TagImplicity.IMPLICIT:
314 tag_implicity = 'implicitTag'
315 elif implicity == TagImplicity.EXPLICIT:
316 tag_implicity = 'explicitTag'
318 assert False, "Unexpected implicity: %s" % implicity
320 type_expr = self.generate_expr(t.type_decl)
321 type_expr += '.subtype(%s=%s)' % (tag_implicity, self.build_tag_expr(t))
325 def inline_selection_type(self, t):
326 selected_type = self.sema_module.resolve_selection_type(t)
327 assert selected_type is not None, "Found no member %s in %s" % (t.identifier, t.type_decl)
329 return self.generate_expr(selected_type)
331 def build_tag_expr(self, tag_def):
332 context = _translate_tag_class(tag_def.class_name)
334 tagged_type_decl = self.sema_module.resolve_type_decl(tag_def.type_decl)
335 if isinstance(tagged_type_decl, ConstructedType):
336 tag_format = 'tag.tagFormatConstructed'
338 tag_format = 'tag.tagFormatSimple'
340 return 'tag.Tag(%s, %s, %s)' % (context, tag_format, tag_def.class_number)
342 def build_constraint_expr(self, constraint):
343 def unpack_size_constraint(nested):
344 if isinstance(nested, SingleValueConstraint):
345 return _translate_value(nested.value), _translate_value(nested.value)
346 elif isinstance(nested, ValueRangeConstraint):
347 return _translate_value(nested.min_value), _translate_value(nested.max_value)
349 assert False, "Unrecognized nested size constraint type: %s" % type(constraint.nested).__name__
351 if isinstance(constraint, SingleValueConstraint):
352 return 'constraint.SingleValueConstraint(%s)' % (_translate_value(constraint.value))
353 elif isinstance(constraint, SizeConstraint):
354 min_value, max_value = unpack_size_constraint(constraint.nested)
355 return 'constraint.ValueSizeConstraint(%s, %s)' % (_translate_value(min_value), _translate_value(max_value))
356 elif isinstance (constraint, ValueRangeConstraint):
357 return 'constraint.ValueRangeConstraint(%s, %s)' % (_translate_value(constraint.min_value),
358 _translate_value(constraint.max_value))
360 assert False, "Unrecognized constraint type: %s" % type(constraint).__name__
362 def build_value_construct_expr(self, type_decl, value):
363 """ Build a valid construct-expression for values, depending on
364 the target pyasn1 type.
367 def build_value_expr(type_name, value):
368 """ Special treatment for bstring and hstring values,
369 which use different construction depending on target type.
371 if isinstance(value, BinaryStringValue):
372 if type_name == 'OCTET STRING':
373 return 'binValue=\'%s\'' % value.value
375 return '"\'%s\'B"' % value.value
376 elif isinstance(value, HexStringValue):
377 if type_name == 'OCTET STRING':
378 return 'hexValue=\'%s\'' % value.value
380 return '"\'%s\'H"' % value.value
382 return _translate_value(value)
384 if isinstance(value, ObjectIdentifierValue):
385 return self.build_object_identifier_value(value)
387 value_type = _translate_type(type_decl.type_name)
388 root_type = self.sema_module.resolve_type_decl(type_decl)
389 return '%s(%s)' % (value_type, build_value_expr(root_type.type_name, value))
391 def inline_component_type(self, t):
392 if t.components_of_type:
393 # COMPONENTS OF works like a literal include, so just
394 # expand all components of the referenced type.
395 included_type_decl = self.sema_module.resolve_type_decl(t.components_of_type)
396 included_content = self.inline_component_types(included_type_decl.components)
398 # Strip trailing newline from inline_component_types
399 # to make the list line up
400 return included_content.strip()
403 return "namedtype.OptionalNamedType('%s', %s)" % (t.identifier, self.generate_expr(t.type_decl))
404 elif t.default_value is not None:
405 type_expr = self.generate_expr(t.type_decl)
406 type_expr += '.subtype(value=%s)' % _translate_value(t.default_value)
408 return "namedtype.DefaultedNamedType('%s', %s)" % (t.identifier, type_expr)
410 return "namedtype.NamedType('%s', %s)" % (t.identifier, self.generate_expr(t.type_decl))
412 def inline_named_type(self, t):
413 return "namedtype.NamedType('%s', %s)" % (t.identifier, self.generate_expr(t.type_decl))
415 def inline_value_list_type(self, t):
416 class_name = _translate_type(t.type_name)
418 named_values = ['(\'%s\', %s)' % (v.identifier, v.value) for v in t.named_values if not isinstance(v, ExtensionMarker)]
419 return '%s(namedValues=namedval.NamedValues(%s))' % (class_name, ', '.join(named_values))
421 return class_name + '()'
423 def inline_sequenceof_type(self, t):
424 return 'univ.SequenceOf(componentType=%s)' % self.generate_expr(t.type_decl)
426 def inline_setof_type(self, t):
427 return 'univ.SetOf(componentType=%s)' % self.generate_expr(t.type_decl)
429 def build_object_identifier_value(self, t):
430 objid_components = []
432 for c in t.components:
433 if isinstance(c, NameForm):
434 if c.name in REGISTERED_OID_NAMES:
435 objid_components.append(str(REGISTERED_OID_NAMES[c.name]))
437 objid_components.append(_translate_value(c.name))
438 elif isinstance(c, NumberForm):
439 objid_components.append(str(c.value))
440 elif isinstance(c, NameAndNumberForm):
441 objid_components.append(str(c.number.value))
445 return '_OID(%s)' % ', '.join(objid_components)
447 def generate_OID(self):
448 fragment = self.writer.get_fragment()
450 fragment.write_line('def _OID(*components):')
451 fragment.push_indent()
452 fragment.write_line('output = []')
453 fragment.write_line('for x in tuple(components):')
454 fragment.push_indent()
455 fragment.write_line('if isinstance(x, univ.ObjectIdentifier):')
456 fragment.push_indent()
457 fragment.write_line('output.extend(list(x))')
458 fragment.pop_indent()
459 fragment.write_line('else:')
460 fragment.push_indent()
461 fragment.write_line('output.append(int(x))')
462 fragment.pop_indent()
463 fragment.pop_indent()
464 fragment.write_blanks(1)
465 fragment.write_line('return univ.ObjectIdentifier(output)')
466 fragment.pop_indent()
468 fragment.pop_indent()
473 def generate_pyasn1(sema_module, out_stream):
474 return Pyasn1Backend(sema_module, out_stream).generate_code()
477 # Translation tables from ASN.1 primitives to pyasn1 primitives
478 _ASN1_TAG_CONTEXTS = {
479 'APPLICATION': 'tag.tagClassApplication',
480 'PRIVATE': 'tag.tagClassPrivate',
481 'UNIVERSAL': 'tag.tagClassUniversal'
485 _ASN1_BUILTIN_VALUES = {
491 _ASN1_BUILTIN_TYPES = {
493 'INTEGER': 'univ.Integer',
494 'BOOLEAN': 'univ.Boolean',
496 'ENUMERATED': 'univ.Enumerated',
498 'BIT STRING': 'univ.BitString',
499 'OCTET STRING': 'univ.OctetString',
500 'CHOICE': 'univ.Choice',
501 'SEQUENCE': 'univ.Sequence',
503 'SEQUENCE OF': 'univ.SequenceOf',
504 'SET OF': 'univ.SetOf',
505 'OBJECT IDENTIFIER': 'univ.ObjectIdentifier',
506 'UTF8String': 'char.UTF8String',
507 'GeneralString': 'char.GeneralString',
508 'NumericString': 'char.NumericString',
509 'PrintableString': 'char.PrintableString',
510 'IA5String': 'char.IA5String',
511 'GraphicString': 'char.GraphicString',
512 'GeneralizedTime': 'useful.GeneralizedTime',
513 'UTCTime': 'useful.UTCTime',
514 'ObjectDescriptor': 'useful.ObjectDescriptor', # In pyasn1 r1.2
518 def _translate_type(type_name):
519 """ Translate ASN.1 built-in types to pyasn1 equivalents.
520 Non-builtins are not translated.
522 assert isinstance(type_name, str), "Type name must be a string."
523 type_name = _sanitize_identifier(type_name)
525 return _ASN1_BUILTIN_TYPES.get(type_name, type_name)
528 def _translate_tag_class(tag_class):
529 """ Translate ASN.1 tag class names to pyasn1 equivalents.
530 Defaults to tag.tagClassContext if tag_class is not
533 return _ASN1_TAG_CONTEXTS.get(tag_class, 'tag.tagClassContext')
536 def _translate_value(value):
537 """ Translate ASN.1 built-in values to Python equivalents.
538 Unrecognized values are not translated.
540 if isinstance(value, ReferencedValue) or _heuristic_is_identifier(value):
541 value = _sanitize_identifier(value)
543 return _ASN1_BUILTIN_VALUES.get(value, value)
546 def _heuristic_is_identifier(value):
547 """ Return True if this value is likely an identifier.
549 first = str(value)[0]
550 return first != '-' and not first.isdigit()
553 def _sanitize_identifier(name):
554 """ Sanitize ASN.1 type and value identifiers so that they're
555 valid Python identifiers.
558 name = name.replace('-', '_')
559 if name in keyword.kwlist:
565 # Simplistic command-line driver
567 with open(args[0]) as f:
570 parse_tree = parser.parse_asn1(asn1def)
572 modules = build_semantic_model(parse_tree)
574 print('WARNING: More than one module generated to the same stream.', file=sys.stderr)
576 for module in modules:
577 print(pygen.auto_generated_header())
578 generate_pyasn1(module, sys.stdout)
583 if __name__ == '__main__':
584 sys.exit(main(sys.argv[1:]))