Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

namedtype.py 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. #
  2. # This file is part of pyasn1 software.
  3. #
  4. # Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
  5. # License: http://snmplabs.com/pyasn1/license.html
  6. #
  7. import sys
  8. from pyasn1 import error
  9. from pyasn1.type import tag
  10. from pyasn1.type import tagmap
  11. __all__ = ['NamedType', 'OptionalNamedType', 'DefaultedNamedType',
  12. 'NamedTypes']
  13. try:
  14. any
  15. except NameError:
  16. any = lambda x: bool(filter(bool, x))
  17. class NamedType(object):
  18. """Create named field object for a constructed ASN.1 type.
  19. The |NamedType| object represents a single name and ASN.1 type of a constructed ASN.1 type.
  20. |NamedType| objects are immutable and duck-type Python :class:`tuple` objects
  21. holding *name* and *asn1Object* components.
  22. Parameters
  23. ----------
  24. name: :py:class:`str`
  25. Field name
  26. asn1Object:
  27. ASN.1 type object
  28. """
  29. isOptional = False
  30. isDefaulted = False
  31. def __init__(self, name, asn1Object, openType=None):
  32. self.__name = name
  33. self.__type = asn1Object
  34. self.__nameAndType = name, asn1Object
  35. self.__openType = openType
  36. def __repr__(self):
  37. representation = '%s=%r' % (self.name, self.asn1Object)
  38. if self.openType:
  39. representation += ' openType: %r' % self.openType
  40. return '<%s object at 0x%x type %s>' % (self.__class__.__name__, id(self), representation)
  41. def __eq__(self, other):
  42. return self.__nameAndType == other
  43. def __ne__(self, other):
  44. return self.__nameAndType != other
  45. def __lt__(self, other):
  46. return self.__nameAndType < other
  47. def __le__(self, other):
  48. return self.__nameAndType <= other
  49. def __gt__(self, other):
  50. return self.__nameAndType > other
  51. def __ge__(self, other):
  52. return self.__nameAndType >= other
  53. def __hash__(self):
  54. return hash(self.__nameAndType)
  55. def __getitem__(self, idx):
  56. return self.__nameAndType[idx]
  57. def __iter__(self):
  58. return iter(self.__nameAndType)
  59. @property
  60. def name(self):
  61. return self.__name
  62. @property
  63. def asn1Object(self):
  64. return self.__type
  65. @property
  66. def openType(self):
  67. return self.__openType
  68. # Backward compatibility
  69. def getName(self):
  70. return self.name
  71. def getType(self):
  72. return self.asn1Object
  73. class OptionalNamedType(NamedType):
  74. __doc__ = NamedType.__doc__
  75. isOptional = True
  76. class DefaultedNamedType(NamedType):
  77. __doc__ = NamedType.__doc__
  78. isDefaulted = True
  79. class NamedTypes(object):
  80. """Create a collection of named fields for a constructed ASN.1 type.
  81. The NamedTypes object represents a collection of named fields of a constructed ASN.1 type.
  82. *NamedTypes* objects are immutable and duck-type Python :class:`dict` objects
  83. holding *name* as keys and ASN.1 type object as values.
  84. Parameters
  85. ----------
  86. *namedTypes: :class:`~pyasn1.type.namedtype.NamedType`
  87. Examples
  88. --------
  89. .. code-block:: python
  90. class Description(Sequence):
  91. '''
  92. ASN.1 specification:
  93. Description ::= SEQUENCE {
  94. surname IA5String,
  95. first-name IA5String OPTIONAL,
  96. age INTEGER DEFAULT 40
  97. }
  98. '''
  99. componentType = NamedTypes(
  100. NamedType('surname', IA5String()),
  101. OptionalNamedType('first-name', IA5String()),
  102. DefaultedNamedType('age', Integer(40))
  103. )
  104. descr = Description()
  105. descr['surname'] = 'Smith'
  106. descr['first-name'] = 'John'
  107. """
  108. def __init__(self, *namedTypes, **kwargs):
  109. self.__namedTypes = namedTypes
  110. self.__namedTypesLen = len(self.__namedTypes)
  111. self.__minTagSet = self.__computeMinTagSet()
  112. self.__nameToPosMap = self.__computeNameToPosMap()
  113. self.__tagToPosMap = self.__computeTagToPosMap()
  114. self.__ambiguousTypes = 'terminal' not in kwargs and self.__computeAmbiguousTypes() or {}
  115. self.__uniqueTagMap = self.__computeTagMaps(unique=True)
  116. self.__nonUniqueTagMap = self.__computeTagMaps(unique=False)
  117. self.__hasOptionalOrDefault = any([True for namedType in self.__namedTypes
  118. if namedType.isDefaulted or namedType.isOptional])
  119. self.__hasOpenTypes = any([True for namedType in self.__namedTypes
  120. if namedType.openType])
  121. self.__requiredComponents = frozenset(
  122. [idx for idx, nt in enumerate(self.__namedTypes) if not nt.isOptional and not nt.isDefaulted]
  123. )
  124. self.__keys = frozenset([namedType.name for namedType in self.__namedTypes])
  125. self.__values = tuple([namedType.asn1Object for namedType in self.__namedTypes])
  126. self.__items = tuple([(namedType.name, namedType.asn1Object) for namedType in self.__namedTypes])
  127. def __repr__(self):
  128. representation = ', '.join(['%r' % x for x in self.__namedTypes])
  129. return '<%s object at 0x%x types %s>' % (self.__class__.__name__, id(self), representation)
  130. def __eq__(self, other):
  131. return self.__namedTypes == other
  132. def __ne__(self, other):
  133. return self.__namedTypes != other
  134. def __lt__(self, other):
  135. return self.__namedTypes < other
  136. def __le__(self, other):
  137. return self.__namedTypes <= other
  138. def __gt__(self, other):
  139. return self.__namedTypes > other
  140. def __ge__(self, other):
  141. return self.__namedTypes >= other
  142. def __hash__(self):
  143. return hash(self.__namedTypes)
  144. def __getitem__(self, idx):
  145. try:
  146. return self.__namedTypes[idx]
  147. except TypeError:
  148. return self.__namedTypes[self.__nameToPosMap[idx]]
  149. def __contains__(self, key):
  150. return key in self.__nameToPosMap
  151. def __iter__(self):
  152. return (x[0] for x in self.__namedTypes)
  153. if sys.version_info[0] <= 2:
  154. def __nonzero__(self):
  155. return self.__namedTypesLen > 0
  156. else:
  157. def __bool__(self):
  158. return self.__namedTypesLen > 0
  159. def __len__(self):
  160. return self.__namedTypesLen
  161. # Python dict protocol
  162. def values(self):
  163. return self.__values
  164. def keys(self):
  165. return self.__keys
  166. def items(self):
  167. return self.__items
  168. def clone(self):
  169. return self.__class__(*self.__namedTypes)
  170. class PostponedError(object):
  171. def __init__(self, errorMsg):
  172. self.__errorMsg = errorMsg
  173. def __getitem__(self, item):
  174. raise error.PyAsn1Error(self.__errorMsg)
  175. def __computeTagToPosMap(self):
  176. tagToPosMap = {}
  177. for idx, namedType in enumerate(self.__namedTypes):
  178. tagMap = namedType.asn1Object.tagMap
  179. if isinstance(tagMap, NamedTypes.PostponedError):
  180. return tagMap
  181. if not tagMap:
  182. continue
  183. for _tagSet in tagMap.presentTypes:
  184. if _tagSet in tagToPosMap:
  185. return NamedTypes.PostponedError('Duplicate component tag %s at %s' % (_tagSet, namedType))
  186. tagToPosMap[_tagSet] = idx
  187. return tagToPosMap
  188. def __computeNameToPosMap(self):
  189. nameToPosMap = {}
  190. for idx, namedType in enumerate(self.__namedTypes):
  191. if namedType.name in nameToPosMap:
  192. return NamedTypes.PostponedError('Duplicate component name %s at %s' % (namedType.name, namedType))
  193. nameToPosMap[namedType.name] = idx
  194. return nameToPosMap
  195. def __computeAmbiguousTypes(self):
  196. ambigiousTypes = {}
  197. partialAmbigiousTypes = ()
  198. for idx, namedType in reversed(tuple(enumerate(self.__namedTypes))):
  199. if namedType.isOptional or namedType.isDefaulted:
  200. partialAmbigiousTypes = (namedType,) + partialAmbigiousTypes
  201. else:
  202. partialAmbigiousTypes = (namedType,)
  203. if len(partialAmbigiousTypes) == len(self.__namedTypes):
  204. ambigiousTypes[idx] = self
  205. else:
  206. ambigiousTypes[idx] = NamedTypes(*partialAmbigiousTypes, **dict(terminal=True))
  207. return ambigiousTypes
  208. def getTypeByPosition(self, idx):
  209. """Return ASN.1 type object by its position in fields set.
  210. Parameters
  211. ----------
  212. idx: :py:class:`int`
  213. Field index
  214. Returns
  215. -------
  216. :
  217. ASN.1 type
  218. Raises
  219. ------
  220. : :class:`~pyasn1.error.PyAsn1Error`
  221. If given position is out of fields range
  222. """
  223. try:
  224. return self.__namedTypes[idx].asn1Object
  225. except IndexError:
  226. raise error.PyAsn1Error('Type position out of range')
  227. def getPositionByType(self, tagSet):
  228. """Return field position by its ASN.1 type.
  229. Parameters
  230. ----------
  231. tagSet: :class:`~pysnmp.type.tag.TagSet`
  232. ASN.1 tag set distinguishing one ASN.1 type from others.
  233. Returns
  234. -------
  235. : :py:class:`int`
  236. ASN.1 type position in fields set
  237. Raises
  238. ------
  239. : :class:`~pyasn1.error.PyAsn1Error`
  240. If *tagSet* is not present or ASN.1 types are not unique within callee *NamedTypes*
  241. """
  242. try:
  243. return self.__tagToPosMap[tagSet]
  244. except KeyError:
  245. raise error.PyAsn1Error('Type %s not found' % (tagSet,))
  246. def getNameByPosition(self, idx):
  247. """Return field name by its position in fields set.
  248. Parameters
  249. ----------
  250. idx: :py:class:`idx`
  251. Field index
  252. Returns
  253. -------
  254. : :py:class:`str`
  255. Field name
  256. Raises
  257. ------
  258. : :class:`~pyasn1.error.PyAsn1Error`
  259. If given field name is not present in callee *NamedTypes*
  260. """
  261. try:
  262. return self.__namedTypes[idx].name
  263. except IndexError:
  264. raise error.PyAsn1Error('Type position out of range')
  265. def getPositionByName(self, name):
  266. """Return field position by filed name.
  267. Parameters
  268. ----------
  269. name: :py:class:`str`
  270. Field name
  271. Returns
  272. -------
  273. : :py:class:`int`
  274. Field position in fields set
  275. Raises
  276. ------
  277. : :class:`~pyasn1.error.PyAsn1Error`
  278. If *name* is not present or not unique within callee *NamedTypes*
  279. """
  280. try:
  281. return self.__nameToPosMap[name]
  282. except KeyError:
  283. raise error.PyAsn1Error('Name %s not found' % (name,))
  284. def getTagMapNearPosition(self, idx):
  285. """Return ASN.1 types that are allowed at or past given field position.
  286. Some ASN.1 serialisation allow for skipping optional and defaulted fields.
  287. Some constructed ASN.1 types allow reordering of the fields. When recovering
  288. such objects it may be important to know which types can possibly be
  289. present at any given position in the field sets.
  290. Parameters
  291. ----------
  292. idx: :py:class:`int`
  293. Field index
  294. Returns
  295. -------
  296. : :class:`~pyasn1.type.tagmap.TagMap`
  297. Map if ASN.1 types allowed at given field position
  298. Raises
  299. ------
  300. : :class:`~pyasn1.error.PyAsn1Error`
  301. If given position is out of fields range
  302. """
  303. try:
  304. return self.__ambiguousTypes[idx].tagMap
  305. except KeyError:
  306. raise error.PyAsn1Error('Type position out of range')
  307. def getPositionNearType(self, tagSet, idx):
  308. """Return the closest field position where given ASN.1 type is allowed.
  309. Some ASN.1 serialisation allow for skipping optional and defaulted fields.
  310. Some constructed ASN.1 types allow reordering of the fields. When recovering
  311. such objects it may be important to know at which field position, in field set,
  312. given *tagSet* is allowed at or past *idx* position.
  313. Parameters
  314. ----------
  315. tagSet: :class:`~pyasn1.type.tag.TagSet`
  316. ASN.1 type which field position to look up
  317. idx: :py:class:`int`
  318. Field position at or past which to perform ASN.1 type look up
  319. Returns
  320. -------
  321. : :py:class:`int`
  322. Field position in fields set
  323. Raises
  324. ------
  325. : :class:`~pyasn1.error.PyAsn1Error`
  326. If *tagSet* is not present or not unique within callee *NamedTypes*
  327. or *idx* is out of fields range
  328. """
  329. try:
  330. return idx + self.__ambiguousTypes[idx].getPositionByType(tagSet)
  331. except KeyError:
  332. raise error.PyAsn1Error('Type position out of range')
  333. def __computeMinTagSet(self):
  334. minTagSet = None
  335. for namedType in self.__namedTypes:
  336. asn1Object = namedType.asn1Object
  337. try:
  338. tagSet = asn1Object.minTagSet
  339. except AttributeError:
  340. tagSet = asn1Object.tagSet
  341. if minTagSet is None or tagSet < minTagSet:
  342. minTagSet = tagSet
  343. return minTagSet or tag.TagSet()
  344. @property
  345. def minTagSet(self):
  346. """Return the minimal TagSet among ASN.1 type in callee *NamedTypes*.
  347. Some ASN.1 types/serialisation protocols require ASN.1 types to be
  348. arranged based on their numerical tag value. The *minTagSet* property
  349. returns that.
  350. Returns
  351. -------
  352. : :class:`~pyasn1.type.tagset.TagSet`
  353. Minimal TagSet among ASN.1 types in callee *NamedTypes*
  354. """
  355. return self.__minTagSet
  356. def __computeTagMaps(self, unique):
  357. presentTypes = {}
  358. skipTypes = {}
  359. defaultType = None
  360. for namedType in self.__namedTypes:
  361. tagMap = namedType.asn1Object.tagMap
  362. if isinstance(tagMap, NamedTypes.PostponedError):
  363. return tagMap
  364. for tagSet in tagMap:
  365. if unique and tagSet in presentTypes:
  366. return NamedTypes.PostponedError('Non-unique tagSet %s of %s at %s' % (tagSet, namedType, self))
  367. presentTypes[tagSet] = namedType.asn1Object
  368. skipTypes.update(tagMap.skipTypes)
  369. if defaultType is None:
  370. defaultType = tagMap.defaultType
  371. elif tagMap.defaultType is not None:
  372. return NamedTypes.PostponedError('Duplicate default ASN.1 type at %s' % (self,))
  373. return tagmap.TagMap(presentTypes, skipTypes, defaultType)
  374. @property
  375. def tagMap(self):
  376. """Return a *TagMap* object from tags and types recursively.
  377. Return a :class:`~pyasn1.type.tagmap.TagMap` object by
  378. combining tags from *TagMap* objects of children types and
  379. associating them with their immediate child type.
  380. Example
  381. -------
  382. .. code-block:: python
  383. OuterType ::= CHOICE {
  384. innerType INTEGER
  385. }
  386. Calling *.tagMap* on *OuterType* will yield a map like this:
  387. .. code-block:: python
  388. Integer.tagSet -> Choice
  389. """
  390. return self.__nonUniqueTagMap
  391. @property
  392. def tagMapUnique(self):
  393. """Return a *TagMap* object from unique tags and types recursively.
  394. Return a :class:`~pyasn1.type.tagmap.TagMap` object by
  395. combining tags from *TagMap* objects of children types and
  396. associating them with their immediate child type.
  397. Example
  398. -------
  399. .. code-block:: python
  400. OuterType ::= CHOICE {
  401. innerType INTEGER
  402. }
  403. Calling *.tagMapUnique* on *OuterType* will yield a map like this:
  404. .. code-block:: python
  405. Integer.tagSet -> Choice
  406. Note
  407. ----
  408. Duplicate *TagSet* objects found in the tree of children
  409. types would cause error.
  410. """
  411. return self.__uniqueTagMap
  412. @property
  413. def hasOptionalOrDefault(self):
  414. return self.__hasOptionalOrDefault
  415. @property
  416. def hasOpenTypes(self):
  417. return self.__hasOpenTypes
  418. @property
  419. def namedTypes(self):
  420. return tuple(self.__namedTypes)
  421. @property
  422. def requiredComponents(self):
  423. return self.__requiredComponents