Skip to content

Instantly share code, notes, and snippets.

@davidwtbuxton
Created April 4, 2018 04:44
Show Gist options
  • Save davidwtbuxton/b3bcab28a55db40600348c103aad9db2 to your computer and use it in GitHub Desktop.
Save davidwtbuxton/b3bcab28a55db40600348c103aad9db2 to your computer and use it in GitHub Desktop.
Changes between Google App Engine SDK version 1.9.67 and 1.9.68
diff -u -r 1.9.67/google_appengine/VERSION 1.9.68/google_appengine/VERSION
--- 1.9.67/google_appengine/VERSION 2018-03-09 20:05:16.000000000 -0800
+++ 1.9.68/google_appengine/VERSION 2018-04-03 21:35:02.000000000 -0700
@@ -1,5 +1,5 @@
-release: "1.9.67"
-timestamp: 1518475832
+release: "1.9.68"
+timestamp: 1519259623
api_versions: ['1']
supported_api_versions:
python:
diff -u -r 1.9.67/google_appengine/_php_runtime.py 1.9.68/google_appengine/_php_runtime.py
--- 1.9.67/google_appengine/_php_runtime.py 2018-03-09 20:05:16.000000000 -0800
+++ 1.9.68/google_appengine/_php_runtime.py 2018-04-03 21:35:02.000000000 -0700
@@ -82,13 +82,6 @@
"""
script_name = os.path.basename(file_path)
-
-
-
-
- if '--api_server_supports_grpc' in sys.argv:
- _PATHS.add_grpc_path(script_name)
-
sys.path = (_PATHS.script_paths(script_name) +
_PATHS.scrub_path(script_name, sys.path))
diff -u -r 1.9.67/google_appengine/_python_runtime.py 1.9.68/google_appengine/_python_runtime.py
--- 1.9.67/google_appengine/_python_runtime.py 2018-03-09 20:05:16.000000000 -0800
+++ 1.9.68/google_appengine/_python_runtime.py 2018-04-03 21:35:02.000000000 -0700
@@ -82,13 +82,6 @@
"""
script_name = os.path.basename(file_path)
-
-
-
-
- if '--api_server_supports_grpc' in sys.argv:
- _PATHS.add_grpc_path(script_name)
-
sys.path = (_PATHS.script_paths(script_name) +
_PATHS.scrub_path(script_name, sys.path))
diff -u -r 1.9.67/google_appengine/dev_appserver.py 1.9.68/google_appengine/dev_appserver.py
--- 1.9.67/google_appengine/dev_appserver.py 2018-03-09 20:05:16.000000000 -0800
+++ 1.9.68/google_appengine/dev_appserver.py 2018-04-03 21:35:02.000000000 -0700
@@ -82,13 +82,6 @@
"""
script_name = os.path.basename(file_path)
-
-
-
-
- if '--api_server_supports_grpc' in sys.argv:
- _PATHS.add_grpc_path(script_name)
-
sys.path = (_PATHS.script_paths(script_name) +
_PATHS.scrub_path(script_name, sys.path))
diff -u -r 1.9.67/google_appengine/google/appengine/datastore/datastore_stub_util.py 1.9.68/google_appengine/google/appengine/datastore/datastore_stub_util.py
--- 1.9.67/google_appengine/google/appengine/datastore/datastore_stub_util.py 2018-03-09 20:05:17.000000000 -0800
+++ 1.9.68/google_appengine/google/appengine/datastore/datastore_stub_util.py 2018-04-03 21:35:02.000000000 -0700
@@ -42,7 +42,9 @@
import atexit
import collections
+import httplib
import itertools
+import json
import logging
import os
import random
@@ -51,6 +53,8 @@
import time
import weakref
+from google.net.proto import ProtocolBuffer
+from google.appengine.datastore import entity_pb
from google.appengine.api import api_base_pb
from google.appengine.api import apiproxy_stub_map
from google.appengine.api import datastore_admin
@@ -65,8 +69,6 @@
from google.appengine.datastore import datastore_stub_index
from google.appengine.datastore import datastore_v4_pb
from google.appengine.runtime import apiproxy_errors
-from google.net.proto import ProtocolBuffer
-from google.appengine.datastore import entity_pb
if datastore_pbs._CLOUD_DATASTORE_ENABLED:
from google.appengine.datastore.datastore_pbs import googledatastore
@@ -179,6 +181,12 @@
_SCATTER_SHIFT = 64 - _MAX_SEQUENTIAL_BIT + 1
+
+
+
+_EMULATOR_CONFIG_CACHE = None
+
+
def _GetScatterProperty(entity_proto):
"""Gets the scatter property for an object.
@@ -2065,6 +2073,9 @@
seed: A hashable object to use as a seed. Use None to use the current
timestamp.
"""
+ self.is_using_cloud_datastore_emulator = False
+ self.emulator_port = None
+
self.SetProbability(probability)
self.SetSeed(seed)
@@ -2079,10 +2090,25 @@
raise TypeError('probability must be a number between 0 and 1, found %r' %
probability)
self._probability = probability
+ if self.is_using_cloud_datastore_emulator:
+ UpdateEmulatorConfig(port=self.emulator_port, consistency_policy=self)
def SetSeed(self, seed):
"""Reset the seed."""
self._random = random.Random(seed)
+ self._seed = seed
+ if self.is_using_cloud_datastore_emulator:
+ UpdateEmulatorConfig(port=self.emulator_port, consistency_policy=self)
+
+ @property
+ def probability(self):
+ """Return the probability of applying a job."""
+ return self._probability
+
+ @property
+ def random_seed(self):
+ """Return the random seed."""
+ return self._seed
def _ShouldApply(self, txn, meta_data):
return self._random.random() < self._probability
@@ -2463,6 +2489,21 @@
pass
+def _CheckAutoIdPolicy(auto_id_policy):
+ """Check value of auto_id_policy.
+
+ Args:
+ auto_id_policy: string constant.
+
+ Raises:
+ TypeError: if auto_id_policy is not one of SEQUENTIAL or SCATTERED.
+ """
+ valid_policies = (SEQUENTIAL, SCATTERED)
+ if auto_id_policy not in valid_policies:
+ raise TypeError('auto_id_policy must be in %s, found %s instead',
+ valid_policies, auto_id_policy)
+
+
class BaseDatastore(BaseTransactionManager, BaseIndexManager):
"""A base implementation of a Datastore.
@@ -2909,10 +2950,7 @@
Raises:
TypeError: if auto_id_policy is not one of SEQUENTIAL or SCATTERED.
"""
- valid_policies = (SEQUENTIAL, SCATTERED)
- if auto_id_policy not in valid_policies:
- raise TypeError('auto_id_policy must be in %s, found %s instead',
- valid_policies, auto_id_policy)
+ _CheckAutoIdPolicy(auto_id_policy)
self._auto_id_policy = auto_id_policy
@@ -5383,3 +5421,81 @@
position.set_before_ascending(
position.before()
!= (first_direction == datastore_pb.Query_Order.DESCENDING))
+
+
+def _CheckConsistencyPolicyForCloudEmulator(consistency_policy):
+ """Check if a consistency policy is supported by GCD Emulator.
+
+ Args:
+ consistency_policy: An instance of PseudoRandomHRConsistencyPolicy or
+ MasterSlaveConsistencyPolicy.
+
+ Raises:
+ UnsupportedConsistencyPolicyError: Consistency policy is not an instance
+ of the above two policies.
+ """
+ if not isinstance(
+ consistency_policy, (
+ MasterSlaveConsistencyPolicy, PseudoRandomHRConsistencyPolicy)):
+ raise TypeError(
+ 'Using Cloud Datastore Emulator, consistency policy must be in in '
+ '(%s, %s), found %s instead' % (
+ MasterSlaveConsistencyPolicy.__name__,
+ PseudoRandomHRConsistencyPolicy.__name__,
+ consistency_policy.__class__))
+
+
+def _BuildEmulatorConfigJson(auto_id_policy=None, consistency_policy=None):
+ """Update the emulator config with its client side cache.
+
+ Args:
+ auto_id_policy: A string indicating how GCD Emulator assigns auto IDs,
+ should be either SEQUENTIAL or SCATTERED.
+ consistency_policy: An instance of PseudoRandomHRConsistencyPolicy or
+ MasterSlaveConsistencyPolicy.
+
+ Returns:
+ A dict representing emulator_config.
+ """
+
+
+ emulator_config = {}
+ if auto_id_policy:
+ _CheckAutoIdPolicy(auto_id_policy)
+ emulator_config['idAllocationPolicy'] = {'policy': auto_id_policy.upper()}
+ if consistency_policy:
+ _CheckConsistencyPolicyForCloudEmulator(consistency_policy)
+ emulator_config['jobPolicy'] = {}
+ if isinstance(consistency_policy, MasterSlaveConsistencyPolicy):
+ emulator_config['jobPolicy']['forceStrongConsistency'] = True
+ else:
+ emulator_config['jobPolicy'] = {
+ 'probabilityJobPolicy': {
+ 'randomSeed': consistency_policy.random_seed,
+ 'unappliedJobPercentage': 100.0 * (
+ 1.0 - consistency_policy.probability)}
+ }
+ return emulator_config
+
+
+def UpdateEmulatorConfig(
+ port, auto_id_policy=None, consistency_policy=None):
+ """Update the cloud datastore emulator's config with its client side cache.
+
+ Args:
+ port: A integer indicating the port number of emulator.
+ auto_id_policy: A string indicating how GCD Emulator assigns auto IDs,
+ should be either SEQUENTIAL or SCATTERED.
+ consistency_policy: An instance of PseudoRandomHRConsistencyPolicy or
+ MasterSlaveConsistencyPolicy.
+ """
+ emulator_config = _BuildEmulatorConfigJson(auto_id_policy, consistency_policy)
+ global _EMULATOR_CONFIG_CACHE
+ if not _EMULATOR_CONFIG_CACHE or _EMULATOR_CONFIG_CACHE != emulator_config:
+ connection = httplib.HTTPConnection('localhost', port)
+ connection.request('PATCH', '/v1/config', json.dumps(emulator_config),
+ {'Content-Type': 'application/json'})
+ response = connection.getresponse()
+
+ response.read()
+ _EMULATOR_CONFIG_CACHE = emulator_config
diff -u -r 1.9.67/google_appengine/google/appengine/ext/remote_api/remote_api_stub.py 1.9.68/google_appengine/google/appengine/ext/remote_api/remote_api_stub.py
--- 1.9.67/google_appengine/google/appengine/ext/remote_api/remote_api_stub.py 2018-03-09 20:05:17.000000000 -0800
+++ 1.9.68/google_appengine/google/appengine/ext/remote_api/remote_api_stub.py 2018-04-03 21:35:03.000000000 -0700
@@ -101,6 +101,7 @@
from google.appengine.api.taskqueue import taskqueue_service_pb
from google.appengine.api.taskqueue import taskqueue_stub
from google.appengine.api.taskqueue import taskqueue_stub_service_pb
+from google.appengine.datastore import datastore_stub_util
from google.appengine.tools import appengine_rpc
_REQUEST_ID_HEADER = 'HTTP_X_APPENGINE_REQUEST_ID'
@@ -570,8 +571,10 @@
"""A stub for testbed calling datastore_v3 service in api_server."""
def __init__(self, server, path,
- max_request_size=apiproxy_stub.MAX_REQUEST_SIZE):
+ max_request_size=apiproxy_stub.MAX_REQUEST_SIZE,
+ emulator_port=None):
super(DatastoreStubTestbedDelegate, self).__init__(server, path)
+ self._emulator_port = emulator_port
self._error_dict = {}
self._error = None
self._error_rate = None
@@ -584,21 +587,31 @@
apiproxy_stub.REQ_SIZE_EXCEEDS_LIMIT_MSG_TEMPLATE % (
service, call))
- def SetConsistencyPolicy(self, policy):
- """A dummy method for backward compatibility with unittests.
-
- The scenario for this test is: running ndb unittest aginst apiserver + cloud
- datastore emulator. The tests triggers stub.SetConsistencyPolicy, which is
- implemented by datastore v3 stubs. See
- //apphosting/datastore/datastore_stub_util.py for the original method
- definition. We don't want to change the tests, just add this fake method to
- pass them. For more details please refer to b/62039789.
+ def SetConsistencyPolicy(self, consistency_policy):
+ """Set the job consistency policy of cloud datastore emulator.
Args:
- policy: A obj inheriting from BaseConsistencyPolicy.
+ consistency_policy: An instance of
+ datastore_stub_util.PseudoRandomHRConsistencyPolicy or
+ datastore_stub_util.MasterSlaveConsistencyPolicy.
"""
+ datastore_stub_util.UpdateEmulatorConfig(
+ port=self._emulator_port, consistency_policy=consistency_policy)
+ if isinstance(consistency_policy,
+ datastore_stub_util.PseudoRandomHRConsistencyPolicy):
+ consistency_policy.is_using_cloud_datastore_emulator = True
+ consistency_policy.emulator_port = self._emulator_port
- pass
+ def SetAutoIdPolicy(self, auto_id_policy):
+ """Set the auto id policy of cloud datastore emulator.
+
+ Args:
+ auto_id_policy: A string indicating how the emulator assigns auto IDs,
+ should be either datastore_stub_util.SCATTERED or
+ datastore_stub_util.SEQUENTIAL.
+ """
+ datastore_stub_util.UpdateEmulatorConfig(
+ port=self._emulator_port, auto_id_policy=auto_id_policy)
def SetTrusted(self, trusted):
"""A dummy method for backward compatibility unittests.
@@ -735,7 +748,7 @@
"""Delegating TaskQueueServiceStub.get_filtered_tasks.
Args:
- url: A String URL that represents the URL all returned tasks point at.
+ url: A string URL that represents the URL all returned tasks point at.
name: The string name of all returned tasks.
queue_names: A string queue_name, or a list of string queue names to
retrieve tasks from. If left blank this will get default to all
diff -u -r 1.9.67/google_appengine/google/appengine/ext/testbed/__init__.py 1.9.68/google_appengine/google/appengine/ext/testbed/__init__.py
--- 1.9.67/google_appengine/google/appengine/ext/testbed/__init__.py 2018-03-09 20:05:17.000000000 -0800
+++ 1.9.68/google_appengine/google/appengine/ext/testbed/__init__.py 2018-04-03 21:35:03.000000000 -0700
@@ -295,9 +295,15 @@
@classmethod
def get_api_port(cls):
+ """Returns the integer port number that api_server listens on."""
return cls._api_port
@classmethod
+ def get_emulator_port(cls):
+ """Returns the integer port number that datastore emulator listens on."""
+ return cls._emulator_port
+
+ @classmethod
def check(cls):
"""Checks whether cloud datastore should be used.
@@ -315,6 +321,7 @@
cls._use_datastore_emulator = True
cls._api_port = int(os.environ['API_SERVER_PORT'])
+ cls._emulator_port = int(os.environ['DATASTORE_EMULATOR_PORT'])
@@ -382,6 +389,7 @@
services=[],
apiproxy=self._test_stub_map,
use_remote_datastore=False)
+ self._emulator_port = EmulatorSupportChecker.get_emulator_port()
def deactivate(self):
"""Deactivates the testbed.
@@ -606,10 +614,20 @@
delegate_stub = (
remote_api_stub.DatastoreStubTestbedDelegate(
self.rpc_server, '/', stub_kw_args.get(
- 'max_request_size', apiproxy_stub.MAX_REQUEST_SIZE)))
+ 'max_request_size', apiproxy_stub.MAX_REQUEST_SIZE),
+ emulator_port=self._emulator_port))
delegate_stub.Clear()
self._test_stub_map.RegisterStub(
DATASTORE_SERVICE_NAME, delegate_stub)
+ consistency_policy = stub_kw_args.get(
+ 'consistency_policy',
+ datastore_stub_util.PseudoRandomHRConsistencyPolicy(probability=1.0))
+ datastore_stub_util.UpdateEmulatorConfig(
+ self._emulator_port, auto_id_policy, consistency_policy)
+ if isinstance(consistency_policy,
+ datastore_stub_util.PseudoRandomHRConsistencyPolicy):
+ consistency_policy.is_using_cloud_datastore_emulator = True
+ consistency_policy.emulator_port = self._emulator_port
self._enabled_stubs[DATASTORE_SERVICE_NAME] = None
diff -u -r 1.9.67/google_appengine/google/appengine/tools/devappserver2/api_server.py 1.9.68/google_appengine/google/appengine/tools/devappserver2/api_server.py
--- 1.9.67/google_appengine/google/appengine/tools/devappserver2/api_server.py 2018-03-09 20:05:17.000000000 -0800
+++ 1.9.68/google_appengine/google/appengine/tools/devappserver2/api_server.py 2018-04-03 21:35:03.000000000 -0700
@@ -44,6 +44,7 @@
import logging
import os
import pickle
+import shutil
import sys
import tempfile
import threading
@@ -75,6 +76,7 @@
from google.appengine.tools.devappserver2 import util
from google.appengine.tools.devappserver2 import wsgi_request_info
from google.appengine.tools.devappserver2 import wsgi_server
+from google.appengine.tools.devappserver2.cloud_emulators import cloud_emulator_manager
# The API lock is applied when calling API stubs that are not threadsafe.
@@ -90,6 +92,10 @@
_FILESAPI_ENABLED = True
+class DatastoreFileError(Exception):
+ """A datastore data file is not supported."""
+
+
def enable_filesapi_tracking(request_data):
"""Turns on per-request tracking of Files API use.
@@ -202,7 +208,7 @@
"""Serves API calls over HTTP and GRPC(optional)."""
def __init__(self, host, port, app_id, use_grpc=False, grpc_api_port=0,
- enable_host_checking=True):
+ enable_host_checking=True, gcd_emulator_launching_thread=None):
self._app_id = app_id
self._host = host
@@ -216,6 +222,7 @@
self._use_grpc = use_grpc
self._grpc_api_port = grpc_api_port
+ self._gcd_emulator_launching_thread = gcd_emulator_launching_thread
def _start_grpc_server(self):
"""Starts gRPC API server."""
@@ -261,6 +268,9 @@
def start(self):
"""Start the API Server."""
+ # If ApiServer uses gcd emulator, emulator must get ready here.
+ if self._gcd_emulator_launching_thread:
+ self._gcd_emulator_launching_thread.join()
super(APIServer, self).start()
logging.info('Starting API server at: http://%s:%d', self._host, self.port)
if self._use_grpc:
@@ -431,12 +441,68 @@
return []
-# An object which is subclass of devappserver2.util.GcdEmulatorManager. If
-# api_server is launched as an individual binary, this should be instantiated by
-# a wrapper that calls api_server.main(). If api_server is launched as part of
-# dev_appserver process, this should be instantiated by a wrapper that calles
-# dev_appserver2.main.
-GCD_EMULATOR_MANAGER = None
+def _launch_gcd_emulator(
+ app_id=None, emulator_port=0, silent=True, index_file='',
+ require_indexes=False, datastore_path='', stub_type=None, cmd=None,
+ is_test=False):
+ """Launch Cloud Datastore emulator asynchronously.
+
+ If datastore_path is sqlite stub data, rename it and convert into emulator
+ data.
+
+ Args:
+ app_id: A string representing application ID.
+ emulator_port: An integer representing the port number for emulator to
+ launch on.
+ silent: A bool indicating if emulator runs in silent mode.
+ index_file: A string indicating the fullpath to index.yaml.
+ require_indexes: A bool. If True, queries that require index generation will
+ fail.
+ datastore_path: A string indicating the path to local datastore data.
+ stub_type: An integer, which is one of
+ datastore_converter.StubTypes.PYTHON_SQLITE_STUB or
+ datastore_converter.StubTypes.JAVA_EMULATOR.
+ cmd: A string representing the path to a executable shell script that
+ invokes the emulator.
+ is_test: A boolean. If True, run emulator in --testing mode for unittests.
+ Otherwise override some emulator flags for dev_appserver use cases.
+
+ Returns:
+ A threading.Thread object that asynchronously launch the emulator.
+ """
+ emulator_host = 'localhost:%d' % emulator_port
+ launching_thread = None
+ emulator_manager = cloud_emulator_manager.DatastoreEmulatorManager(
+ cmd=cmd, is_test=is_test)
+ emulator_manager.CheckOptions(index_file=index_file,
+ storage_file=datastore_path)
+ def _launch(need_conversion):
+ """Launch the emulator and convert SQLite data if needed.
+
+ Args:
+ need_conversion: A bool. If True convert sqlite data to emulator data.
+ """
+ emulator_manager.Launch(
+ emulator_port, silent, index_file, require_indexes, datastore_path)
+ if need_conversion:
+ logging.info(
+ 'Converting datastore_sqlite_stub data in %s to Cloud Datastore '
+ 'emulator data in %s.', sqlite_data_renamed, datastore_path)
+ datastore_converter.convert_sqlite_data_to_emulator(
+ app_id, sqlite_data_renamed, emulator_host)
+ # Shutdown datastore emulator before dev_appserver quits.
+ shutdown.extra_shutdown_callback.append(emulator_manager.Shutdown)
+
+ if stub_type == datastore_converter.StubTypes.PYTHON_SQLITE_STUB:
+ sqlite_data_renamed = datastore_path + '.sqlitestub'
+ shutil.move(datastore_path, sqlite_data_renamed)
+ logging.info('SQLite stub data has been renamed to %s', sqlite_data_renamed)
+ launching_thread = threading.Thread(target=_launch, args=[True])
+ else:
+ launching_thread = threading.Thread(target=_launch, args=[False])
+ launching_thread.start()
+ os.environ['DATASTORE_EMULATOR_HOST'] = emulator_host
+ return launching_thread
def create_api_server(
@@ -456,22 +522,13 @@
Returns:
An instance of APIServer.
+
+ Raises:
+ DatastoreFileError: Cloud Datastore emulator is used while stub_type is
+ datastore_converter.StubTypes.PYTHON_FILE_STUB.
"""
- emulator_launching_thread = None
- if options.support_datastore_emulator and not os.environ.get(
- 'DATASTORE_EMULATOR_HOST'):
- gcd_emulator_port = portpicker.PickUnusedPort()
- emulator_launching_thread = threading.Thread(
- target=GCD_EMULATOR_MANAGER.launch,
- args=[
- gcd_emulator_port,
- options.dev_appserver_log_level != 'debug',
- os.path.join(app_root, 'index.yaml'), options.require_indexes])
- emulator_launching_thread.start()
- os.environ['DATASTORE_EMULATOR_HOST'] = 'localhost:%d' % gcd_emulator_port
+ datastore_path = get_datastore_path(storage_path, options.datastore_path)
- datastore_path = options.datastore_path or os.path.join(
- storage_path, 'datastore.db')
logs_path = options.logs_path or os.path.join(storage_path, 'logs.db')
search_index_path = options.search_indexes_path or os.path.join(
storage_path, 'search_indexes')
@@ -505,24 +562,46 @@
assert 0, ('unknown consistency policy: %r' %
options.datastore_consistency_policy)
- # Check if local datastore data should be converted.
- # Using GCD Emulator this could convert python file stub or sqlite stub data
- # to Emulator data format; Without GCD Emulator this converts python file stub
- # to sqlite stub data.
- if os.path.exists(datastore_path):
- data_type = datastore_converter.get_data_type(datastore_path)
- if options.support_datastore_emulator:
- if data_type in [datastore_converter.StubTypes.PYTHON_FILE_STUB,
- datastore_converter.StubTypes.PYTHON_SQLITE_STUB]:
- if emulator_launching_thread:
- emulator_launching_thread.join()
- gcd_emulator_host = os.environ.get('DATASTORE_EMULATOR_HOST')
- datastore_converter.convert_python_data_to_emulator(
- app_id, data_type, datastore_path, gcd_emulator_host)
+ stub_type = (datastore_converter.get_stub_type(datastore_path)
+ if os.path.exists(datastore_path) else None)
+ gcd_emulator_launching_thread = None
+ if options.support_datastore_emulator:
+ if stub_type == datastore_converter.StubTypes.PYTHON_FILE_STUB:
+ raise DatastoreFileError(
+ 'The datastore file %s cannot be recognized by dev_appserver. Please '
+ 'restart dev_appserver with --clear_datastore=1' % datastore_path)
+ env_emulator_host = os.environ.get('DATASTORE_EMULATOR_HOST')
+ if env_emulator_host: # emulator already running, reuse it.
+ logging.warning(
+ 'Detected environment variable DATASTORE_EMULATOR_HOST=%s, '
+ 'dev_appserver will speak to the Cloud Datastore emulator running on '
+ 'this address. The datastore_path %s will be neglected.\nIf you '
+ 'want datastore to store on %s, remove DATASTORE_EMULATOR_HOST '
+ 'from environment variables and restart dev_appserver',
+ env_emulator_host, datastore_path, datastore_path)
else:
- if data_type != datastore_converter.StubTypes.PYTHON_SQLITE_STUB:
- datastore_converter.convert_datastore_file_stub_data_to_sqlite(
- app_id, datastore_path)
+ gcd_emulator_launching_thread = _launch_gcd_emulator(
+ app_id=app_id,
+ emulator_port=(
+ options.gcd_emulator_port if options.gcd_emulator_port else
+ portpicker.PickUnusedPort()),
+ silent=options.dev_appserver_log_level != 'debug',
+ index_file=os.path.join(app_root, 'index.yaml'),
+ require_indexes=options.require_indexes,
+ datastore_path=datastore_path,
+ stub_type=stub_type,
+ cmd=options.gcd_emulator_cmd,
+ is_test=options.gcd_emulator_is_test_mode)
+ else:
+ # Use SQLite stub.
+ # For historic reason we are still supporting conversion from file stub to
+ # SQLite stub data. But this conversion will go away.
+
+
+
+ if stub_type == datastore_converter.StubTypes.PYTHON_FILE_STUB:
+ datastore_converter.convert_datastore_file_stub_data_to_sqlite(
+ app_id, datastore_path)
stub_util.setup_stubs(
request_data=request_info,
@@ -560,13 +639,11 @@
datastore_grpc_stub.DatastoreGrpcStub
if options.support_datastore_emulator else None)
)
-
- if emulator_launching_thread:
- emulator_launching_thread.join()
return APIServer(
options.api_host, options.api_port, app_id,
options.api_server_supports_grpc or options.support_datastore_emulator,
- options.grpc_api_port, options.enable_host_checking)
+ options.grpc_api_port, options.enable_host_checking,
+ gcd_emulator_launching_thread)
def _clear_datastore_storage(datastore_path):
@@ -623,6 +700,19 @@
return path
+def get_datastore_path(storage_path, datastore_path):
+ """Gets the path to datastore data file.
+
+ Args:
+ storage_path: A string directory for storing API stub data.
+ datastore_path: A string representing the path to local datastore file.
+
+ Returns:
+ A string representing the path to datastore data file.
+ """
+ return datastore_path or os.path.join(storage_path, 'datastore.db')
+
+
def _generate_storage_paths(app_id):
"""Yield an infinite sequence of possible storage paths."""
if sys.platform == 'win32':
@@ -644,12 +734,15 @@
yield os.path.join(tempdir, 'appengine.%s%s.%d' % (app_id, user_format, i))
+PARSER = cli_parser.create_command_line_parser(
+ cli_parser.API_SERVER_CONFIGURATION)
+
+
def main():
"""Parses command line options and launches the API server."""
shutdown.install_signal_handlers()
- options = cli_parser.create_command_line_parser(
- cli_parser.API_SERVER_CONFIGURATION).parse_args()
+ options = PARSER.parse_args()
logging.getLogger().setLevel(
constants.LOG_LEVEL_TO_PYTHON_CONSTANT[options.dev_appserver_log_level])
diff -u -r 1.9.67/google_appengine/google/appengine/tools/devappserver2/cli_parser.py 1.9.68/google_appengine/google/appengine/tools/devappserver2/cli_parser.py
--- 1.9.67/google_appengine/google/appengine/tools/devappserver2/cli_parser.py 2018-03-09 20:05:17.000000000 -0800
+++ 1.9.68/google_appengine/google/appengine/tools/devappserver2/cli_parser.py 2018-04-03 21:35:03.000000000 -0700
@@ -668,6 +668,31 @@
'release. Please do not rely on sequential IDs in your '
'tests.')
+
+
+
+ datastore_group.add_argument(
+ '--support_datastore_emulator',
+ action=boolean_action.BooleanAction,
+ const=True,
+ default=False,
+ help=argparse.SUPPRESS)
+ # Port number on which dev_appserver should launch Cloud Datastore emulator.
+ datastore_group.add_argument(
+ '--gcd_emulator_port', type=PortParser(), default=0,
+ help=argparse.SUPPRESS)
+ # The path to an executable shell script that invokes Cloud Datastore
+ # emulator.
+ datastore_group.add_argument(
+ '--gcd_emulator_cmd', type=parse_path, default=None,
+ help=argparse.SUPPRESS)
+ datastore_group.add_argument(
+ '--gcd_emulator_is_test_mode',
+ action=boolean_action.BooleanAction,
+ const=True,
+ default=False,
+ help=argparse.SUPPRESS)
+
# Logs
logs_group = parser.add_argument_group('Logs API')
logs_group.add_argument(
@@ -768,16 +793,6 @@
const=True,
default=False,
help=argparse.SUPPRESS)
-
-
-
-
- misc_group.add_argument(
- '--support_datastore_emulator',
- action=boolean_action.BooleanAction,
- const=True,
- default=False,
- help=argparse.SUPPRESS)
misc_group.add_argument(
'--grpc_api_port', type=PortParser(), default=0,
help='port on which the gRPC API server listens.')
Only in 1.9.68/google_appengine/google/appengine/tools/devappserver2: cloud_emulators
diff -u -r 1.9.67/google_appengine/google/appengine/tools/devappserver2/datastore_converter.py 1.9.68/google_appengine/google/appengine/tools/devappserver2/datastore_converter.py
--- 1.9.67/google_appengine/google/appengine/tools/devappserver2/datastore_converter.py 2018-03-09 20:05:17.000000000 -0800
+++ 1.9.68/google_appengine/google/appengine/tools/devappserver2/datastore_converter.py 2018-04-03 21:35:03.000000000 -0700
@@ -16,6 +16,7 @@
#
"""Methods for converting GAE local datastore data into GCD Emulator data."""
+import httplib
import logging
import os
import shutil
@@ -34,6 +35,10 @@
JAVA_STREAM_MAGIC = '\xac\xed'
+class PersistException(Exception):
+ """Raised when calls to cloud datastore emulator's /persist endpoint fail."""
+
+
class StubTypes(object):
"""Possible types of stub/emulator local datastore data."""
# Data of datastore_file_stub
@@ -45,7 +50,7 @@
JAVA_EMULATOR = 2
-def get_data_type(filename):
+def get_stub_type(filename):
"""Determine which type of datastore fake a local data file belongs to.
Args:
@@ -75,46 +80,43 @@
return StubTypes.PYTHON_FILE_STUB
-def convert_python_data_to_emulator(
- app_id, stub_type, filename, gcd_emulator_host):
- """Convert datastore_file_stub or datastore_sqlite_stub data to emulator data.
+def convert_sqlite_data_to_emulator(app_id, filename, gcd_emulator_host):
+ """Convert datastore sqlite stub data to cloud emulator data.
Args:
app_id: A String representing application ID.
- stub_type: A String representing the stub type filename belongs to.
- filename: A String representing the absolute path to local data.
+ filename: A String representing the absolute path to SQLite data.
gcd_emulator_host: A String in the format of host:port indicate the hostname
and port number of gcd emulator.
+
+ Raises:
+ PersistException: if the call to emulator's /persist endpoint fails.
"""
previous_stub = apiproxy_stub_map.apiproxy.GetStub('datastore_v3')
- try:
- if stub_type == StubTypes.PYTHON_FILE_STUB:
- logging.info(
- 'Converting datastore_file_stub data to cloud datastore emulator '
- 'data.')
- python_stub = datastore_file_stub.DatastoreFileStub(
- app_id, filename, trusted=True, save_changes=False)
- else: # Sqlite stub
- logging.info(
- 'Converting datastore_sqlite_stub data to cloud datastore emulator '
- 'data.')
- python_stub = datastore_sqlite_stub.DatastoreSqliteStub(
- app_id, filename, trusted=True, use_atexit=False)
- apiproxy_stub_map.apiproxy.ReplaceStub('datastore_v3', python_stub)
- entities = _fetch_all_datastore_entities()
+ sqlite_stub = datastore_sqlite_stub.DatastoreSqliteStub(
+ app_id, filename, trusted=True, use_atexit=False)
+ apiproxy_stub_map.apiproxy.ReplaceStub('datastore_v3', sqlite_stub)
+ entities = _fetch_all_datastore_entities()
+ if entities:
+ logging.info('Fetched %d entities from %s', len(entities), filename)
grpc_stub = datastore_grpc_stub.DatastoreGrpcStub(gcd_emulator_host)
grpc_stub.get_or_set_call_handler_stub()
apiproxy_stub_map.apiproxy.ReplaceStub('datastore_v3', grpc_stub)
datastore.Put(entities)
- logging.info('Conversion complete.')
- python_stub.Close()
- finally:
-
-
-
- apiproxy_stub_map.apiproxy.ReplaceStub('datastore_v3', previous_stub)
- logging.info('Datastore conversion complete')
+ # persist entities to disk in emulator's data format.
+ conn = httplib.HTTPConnection(gcd_emulator_host)
+ conn.request('POST', '/persist')
+ response = conn.getresponse()
+ msg = response.read()
+ if httplib.OK != response.status:
+ raise PersistException(msg)
+ logging.info('Datastore conversion complete')
+ else:
+ logging.warning('Fetched 0 entity from %s, will not create cloud '
+ 'datastore emulator file', filename)
+ sqlite_stub.Close()
+ apiproxy_stub_map.apiproxy.ReplaceStub('datastore_v3', previous_stub)
def convert_datastore_file_stub_data_to_sqlite(app_id, datastore_file):
@@ -151,12 +153,11 @@
apiproxy_stub_map.apiproxy.ReplaceStub('datastore_v3', previous_stub)
- back_up_file_name = datastore_file + '.filestub'
- shutil.copy(datastore_file, back_up_file_name)
- os.remove(datastore_file)
+ file_stub_data_renamed = datastore_file + '.filestub'
+ shutil.move(datastore_file, file_stub_data_renamed)
shutil.move(sqlite_file_name, datastore_file)
- logging.info('Datastore conversion complete. File stub data has been backed '
- 'up in %s', back_up_file_name)
+ logging.info('Datastore conversion complete. File stub data has been renamed '
+ 'to %s', file_stub_data_renamed)
def _fetch_all_datastore_entities():
diff -u -r 1.9.67/google_appengine/google/appengine/tools/devappserver2/devappserver2.py 1.9.68/google_appengine/google/appengine/tools/devappserver2/devappserver2.py
--- 1.9.67/google_appengine/google/appengine/tools/devappserver2/devappserver2.py 2018-03-09 20:05:17.000000000 -0800
+++ 1.9.68/google_appengine/google/appengine/tools/devappserver2/devappserver2.py 2018-04-03 21:35:03.000000000 -0700
@@ -28,6 +28,7 @@
from google.appengine.tools.devappserver2 import application_configuration
from google.appengine.tools.devappserver2 import cli_parser
from google.appengine.tools.devappserver2 import constants
+from google.appengine.tools.devappserver2 import datastore_converter
from google.appengine.tools.devappserver2 import dispatcher
from google.appengine.tools.devappserver2 import metrics
from google.appengine.tools.devappserver2 import runtime_config_pb2
@@ -49,6 +50,17 @@
cli_parser.DEV_APPSERVER_CONFIGURATION)
+class PhpVersionError(Exception):
+ """Raised when multiple versions of php are in app yaml."""
+
+
+class PhpPathError(Exception):
+ """Raised when --php_executable_path is not specified for php72.
+
+ This flag is optional for php55.
+ """
+
+
class DevelopmentServer(object):
"""Encapsulates the logic for the development server.
@@ -80,6 +92,9 @@
Args:
options: An argparse.Namespace containing the command line arguments.
+
+ Raises:
+ PhpPathError: php executable path is not specified for php72.
"""
self._options = options
@@ -93,13 +108,20 @@
runtime=options.runtime,
env_variables=parsed_env_variables)
+ storage_path = api_server.get_storage_path(
+ options.storage_path, configuration.app_id)
+ datastore_path = api_server.get_datastore_path(
+ storage_path, options.datastore_path)
+ datastore_data_type = (datastore_converter.get_stub_type(datastore_path)
+ if os.path.isfile(datastore_path) else None)
if options.google_analytics_client_id:
metrics_logger = metrics.GetMetricsLogger()
metrics_logger.Start(
options.google_analytics_client_id,
options.google_analytics_user_agent,
{module.runtime for module in configuration.modules},
- {module.env or 'standard' for module in configuration.modules})
+ {module.env or 'standard' for module in configuration.modules},
+ options.support_datastore_emulator, datastore_data_type)
if options.skip_sdk_update_check:
logging.info('Skipping SDK update check.')
@@ -123,6 +145,10 @@
util.setup_environ(configuration.app_id)
+ php_version = self._get_php_runtime(configuration)
+ if not options.php_executable_path and php_version == 'php72':
+ raise PhpPathError('For php72, --php_executable_path must be specified.')
+
self._dispatcher = dispatcher.Dispatcher(
configuration, options.host, options.port, options.auth_domain,
constants.LOG_LEVEL_TO_RUNTIME_CONSTANT[options.log_level],
@@ -131,7 +157,7 @@
- self._create_php_config(options),
+ self._create_php_config(options, php_version),
self._create_python_config(options),
self._create_java_config(options),
self._create_go_config(options),
@@ -151,8 +177,6 @@
options.enable_host_checking)
wsgi_request_info_ = wsgi_request_info.WSGIRequestInfo(self._dispatcher)
- storage_path = api_server.get_storage_path(
- options.storage_path, configuration.app_id)
apiserver = api_server.create_api_server(
wsgi_request_info_, storage_path, options, configuration.app_id,
@@ -211,7 +235,44 @@
metrics.GetMetricsLogger().Stop(**kwargs)
@staticmethod
- def _create_php_config(options):
+ def _get_php_runtime(config):
+ """Get the php runtime specified in user application.
+
+ Currently we only allow one version of php although devappserver supports
+ running multiple services.
+
+ Args:
+ config: An instance of application_configuration.ApplicationConfiguration.
+
+ Returns:
+ A string representing name of the runtime.
+
+ Raises:
+ PhpVersionError: More than one version of php is found in app yaml.
+ """
+ runtime = None
+ for module_configuration in config.modules:
+ r = module_configuration.runtime
+ if r.startswith('php'):
+ if not runtime:
+ runtime = r
+ elif runtime != r:
+ raise PhpVersionError(
+ 'Found both %s and %s in yaml files, you can run only choose one '
+ 'version of php on dev_appserver.' % (runtime, r))
+ return runtime
+
+ @staticmethod
+ def _create_php_config(options, php_version=None):
+ """Create a runtime_config.PhpConfig based on flag and php_version.
+
+ Args:
+ options: An argparse.Namespace object.
+ php_version: A string representing php version.
+
+ Returns:
+ A runtime_config.PhpConfig object.
+ """
php_config = runtime_config_pb2.PhpConfig()
if options.php_executable_path:
php_config.php_executable_path = os.path.abspath(
@@ -223,6 +284,8 @@
if options.php_xdebug_extension_path:
php_config.xdebug_extension_path = os.path.abspath(
options.php_xdebug_extension_path)
+ if php_version:
+ php_config.php_version = php_version
return php_config
diff -u -r 1.9.67/google_appengine/google/appengine/tools/devappserver2/metrics.py 1.9.68/google_appengine/google/appengine/tools/devappserver2/metrics.py
--- 1.9.67/google_appengine/google/appengine/tools/devappserver2/metrics.py 2018-03-09 20:05:17.000000000 -0800
+++ 1.9.68/google_appengine/google/appengine/tools/devappserver2/metrics.py 2018-04-03 21:35:03.000000000 -0700
@@ -97,6 +97,8 @@
'IsDevShell': 'cd7',
'Platform': 'cd8',
'Is64Bits': 'cd9',
+ 'SupportDatastoreEmulator': 'cd10',
+ 'DatastoreDataType': 'cd11',
}
# Devappserver Google Analytics Custom Metrics.
@@ -125,11 +127,14 @@
self._is_dev_shell = constants.DEVSHELL_ENV in os.environ
self._is_64_bits = sys.maxsize > 2**32
self._platform = platform.platform()
+ self._support_datastore_emulator = None
+ self._datastore_data_type = None
# Stores events for batch logging once Stop has been called.
self._log_once_on_stop_events = {}
- def Start(self, client_id, user_agent=None, runtimes=None, environment=None):
+ def Start(self, client_id, user_agent=None, runtimes=None, environment=None,
+ support_datastore_emulator=None, datastore_data_type=None):
"""Starts a Google Analytics session for the current client.
Args:
@@ -137,12 +142,18 @@
user_agent: A string user agent to send with each log.
runtimes: A set of strings containing the runtimes used.
environment: A set of strings containing the environments used.
+ support_datastore_emulator: A boolean indicating whether dev_appserver
+ supports Cloud Datastore emulator.
+ datastore_data_type: A string representing the type of data for local
+ datastore file.
"""
self._client_id = client_id
self._user_agent = user_agent
self._runtimes = ','.join(sorted(list(runtimes))) if runtimes else None
self._environment = ','.join(
sorted(list(environment))) if environment else None
+ self._support_datastore_emulator = support_datastore_emulator
+ self._datastore_data_type = datastore_data_type
self.Log(DEVAPPSERVER_CATEGORY, START_ACTION)
self._start_time = Now()
@@ -264,6 +275,10 @@
GOOGLE_ANALYTICS_DIMENSIONS['IsDevShell']: self._is_dev_shell,
GOOGLE_ANALYTICS_DIMENSIONS['Platform']: self._platform,
GOOGLE_ANALYTICS_DIMENSIONS['Is64Bits']: self._is_64_bits,
+ GOOGLE_ANALYTICS_DIMENSIONS[
+ 'SupportDatastoreEmulator']: self._support_datastore_emulator,
+ GOOGLE_ANALYTICS_DIMENSIONS[
+ 'DatastoreDataType']: self._datastore_data_type,
# Required event data
'ec': category,
'ea': action
diff -u -r 1.9.67/google_appengine/google/appengine/tools/devappserver2/php/runtime/runtime.py 1.9.68/google_appengine/google/appengine/tools/devappserver2/php/runtime/runtime.py
--- 1.9.67/google_appengine/google/appengine/tools/devappserver2/php/runtime/runtime.py 2018-03-09 20:05:17.000000000 -0800
+++ 1.9.68/google_appengine/google/appengine/tools/devappserver2/php/runtime/runtime.py 2018-04-03 21:35:03.000000000 -0700
@@ -92,6 +92,8 @@
'STDERR_LOG_LEVEL': str(config.stderr_log_level),
'TZ': 'UTC',
}
+ if config.php_config.php_version == 'php72':
+ self.environ_template['GAE_APPLICATION'] = str(config.app_id)
self.environ_template.update((env.key, env.value) for env in config.environ)
def make_php_cgi_environ(self, environ):
diff -u -r 1.9.67/google_appengine/google/appengine/tools/devappserver2/runtime_config_pb2.py 1.9.68/google_appengine/google/appengine/tools/devappserver2/runtime_config_pb2.py
--- 1.9.67/google_appengine/google/appengine/tools/devappserver2/runtime_config_pb2.py 2018-03-09 20:05:17.000000000 -0800
+++ 1.9.68/google_appengine/google/appengine/tools/devappserver2/runtime_config_pb2.py 2018-04-03 21:35:03.000000000 -0700
@@ -36,7 +36,7 @@
name='apphosting/tools/devappserver2/runtime_config.proto',
package='apphosting.tools.devappserver2',
syntax='proto2',
- serialized_pb=_b('\n3apphosting/tools/devappserver2/runtime_config.proto\x12\x1e\x61pphosting.tools.devappserver2\"\xee\x07\n\x06\x43onfig\x12\x0e\n\x06\x61pp_id\x18\x01 \x02(\x0c\x12\x12\n\nversion_id\x18\x02 \x02(\x0c\x12\x18\n\x10\x61pplication_root\x18\x03 \x02(\x0c\x12\x19\n\nthreadsafe\x18\x04 \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x08\x61pi_host\x18\x11 \x01(\t:\tlocalhost\x12\x10\n\x08\x61pi_port\x18\x05 \x02(\x05\x12:\n\tlibraries\x18\x06 \x03(\x0b\x32\'.apphosting.tools.devappserver2.Library\x12\x16\n\nskip_files\x18\x07 \x01(\t:\x02^$\x12\x18\n\x0cstatic_files\x18\x08 \x01(\t:\x02^$\x12\x43\n\rpython_config\x18\x0e \x01(\x0b\x32,.apphosting.tools.devappserver2.PythonConfig\x12=\n\nphp_config\x18\t \x01(\x0b\x32).apphosting.tools.devappserver2.PhpConfig\x12?\n\x0bnode_config\x18\x1a \x01(\x0b\x32*.apphosting.tools.devappserver2.NodeConfig\x12?\n\x0bjava_config\x18\x15 \x01(\x0b\x32*.apphosting.tools.devappserver2.JavaConfig\x12\x43\n\rcustom_config\x18\x17 \x01(\x0b\x32,.apphosting.tools.devappserver2.CustomConfig\x12;\n\tgo_config\x18\x19 \x01(\x0b\x32(.apphosting.tools.devappserver2.GoConfig\x12\x38\n\x07\x65nviron\x18\n \x03(\x0b\x32\'.apphosting.tools.devappserver2.Environ\x12\x42\n\x10\x63loud_sql_config\x18\x0b \x01(\x0b\x32(.apphosting.tools.devappserver2.CloudSQL\x12\x12\n\ndatacenter\x18\x0c \x02(\t\x12\x13\n\x0binstance_id\x18\r \x02(\t\x12\x1b\n\x10stderr_log_level\x18\x0f \x01(\x03:\x01\x31\x12\x13\n\x0b\x61uth_domain\x18\x10 \x02(\t\x12\x15\n\rmax_instances\x18\x12 \x01(\x05\x12;\n\tvm_config\x18\x13 \x01(\x0b\x32(.apphosting.tools.devappserver2.VMConfig\x12\x13\n\x0bserver_port\x18\x14 \x01(\x05\x12\x11\n\x02vm\x18\x16 \x01(\x08:\x05\x66\x61lse\x12\x11\n\tgrpc_apis\x18\x18 \x03(\t\"|\n\tPhpConfig\x12\x1b\n\x13php_executable_path\x18\x01 \x01(\x0c\x12\x17\n\x0f\x65nable_debugger\x18\x03 \x02(\x08\x12\x1a\n\x12gae_extension_path\x18\x04 \x01(\x0c\x12\x1d\n\x15xdebug_extension_path\x18\x05 \x01(\x0c\"*\n\nNodeConfig\x12\x1c\n\x14node_executable_path\x18\x01 \x01(\x0c\"<\n\x0cPythonConfig\x12\x16\n\x0estartup_script\x18\x01 \x01(\t\x12\x14\n\x0cstartup_args\x18\x02 \x01(\t\"\x1e\n\nJavaConfig\x12\x10\n\x08jvm_args\x18\x01 \x03(\t\"W\n\x08GoConfig\x12\x10\n\x08work_dir\x18\x01 \x01(\t\x12\x1f\n\x17\x65nable_watching_go_path\x18\x02 \x01(\x08\x12\x18\n\x10\x65nable_debugging\x18\x03 \x01(\x08\":\n\x0c\x43ustomConfig\x12\x19\n\x11\x63ustom_entrypoint\x18\x01 \x01(\t\x12\x0f\n\x07runtime\x18\x02 \x01(\t\"t\n\x08\x43loudSQL\x12\x12\n\nmysql_host\x18\x01 \x02(\t\x12\x12\n\nmysql_port\x18\x02 \x02(\x05\x12\x12\n\nmysql_user\x18\x03 \x02(\t\x12\x16\n\x0emysql_password\x18\x04 \x02(\t\x12\x14\n\x0cmysql_socket\x18\x05 \x01(\t\"(\n\x07Library\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x0f\n\x07version\x18\x02 \x02(\t\"%\n\x07\x45nviron\x12\x0b\n\x03key\x18\x01 \x02(\x0c\x12\r\n\x05value\x18\x02 \x02(\x0c\":\n\x08VMConfig\x12\x19\n\x11\x64ocker_daemon_url\x18\x01 \x01(\t\x12\x13\n\x0b\x65nable_logs\x18\x03 \x01(\x08\x42\x32\n,com.google.appengine.tools.development.proto \x02P\x01')
+ serialized_pb=_b('\n3apphosting/tools/devappserver2/runtime_config.proto\x12\x1e\x61pphosting.tools.devappserver2\"\xee\x07\n\x06\x43onfig\x12\x0e\n\x06\x61pp_id\x18\x01 \x02(\x0c\x12\x12\n\nversion_id\x18\x02 \x02(\x0c\x12\x18\n\x10\x61pplication_root\x18\x03 \x02(\x0c\x12\x19\n\nthreadsafe\x18\x04 \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x08\x61pi_host\x18\x11 \x01(\t:\tlocalhost\x12\x10\n\x08\x61pi_port\x18\x05 \x02(\x05\x12:\n\tlibraries\x18\x06 \x03(\x0b\x32\'.apphosting.tools.devappserver2.Library\x12\x16\n\nskip_files\x18\x07 \x01(\t:\x02^$\x12\x18\n\x0cstatic_files\x18\x08 \x01(\t:\x02^$\x12\x43\n\rpython_config\x18\x0e \x01(\x0b\x32,.apphosting.tools.devappserver2.PythonConfig\x12=\n\nphp_config\x18\t \x01(\x0b\x32).apphosting.tools.devappserver2.PhpConfig\x12?\n\x0bnode_config\x18\x1a \x01(\x0b\x32*.apphosting.tools.devappserver2.NodeConfig\x12?\n\x0bjava_config\x18\x15 \x01(\x0b\x32*.apphosting.tools.devappserver2.JavaConfig\x12\x43\n\rcustom_config\x18\x17 \x01(\x0b\x32,.apphosting.tools.devappserver2.CustomConfig\x12;\n\tgo_config\x18\x19 \x01(\x0b\x32(.apphosting.tools.devappserver2.GoConfig\x12\x38\n\x07\x65nviron\x18\n \x03(\x0b\x32\'.apphosting.tools.devappserver2.Environ\x12\x42\n\x10\x63loud_sql_config\x18\x0b \x01(\x0b\x32(.apphosting.tools.devappserver2.CloudSQL\x12\x12\n\ndatacenter\x18\x0c \x02(\t\x12\x13\n\x0binstance_id\x18\r \x02(\t\x12\x1b\n\x10stderr_log_level\x18\x0f \x01(\x03:\x01\x31\x12\x13\n\x0b\x61uth_domain\x18\x10 \x02(\t\x12\x15\n\rmax_instances\x18\x12 \x01(\x05\x12;\n\tvm_config\x18\x13 \x01(\x0b\x32(.apphosting.tools.devappserver2.VMConfig\x12\x13\n\x0bserver_port\x18\x14 \x01(\x05\x12\x11\n\x02vm\x18\x16 \x01(\x08:\x05\x66\x61lse\x12\x11\n\tgrpc_apis\x18\x18 \x03(\t\"\x91\x01\n\tPhpConfig\x12\x1b\n\x13php_executable_path\x18\x01 \x01(\x0c\x12\x17\n\x0f\x65nable_debugger\x18\x03 \x02(\x08\x12\x1a\n\x12gae_extension_path\x18\x04 \x01(\x0c\x12\x1d\n\x15xdebug_extension_path\x18\x05 \x01(\x0c\x12\x13\n\x0bphp_version\x18\x06 \x01(\x0c\"*\n\nNodeConfig\x12\x1c\n\x14node_executable_path\x18\x01 \x01(\x0c\"<\n\x0cPythonConfig\x12\x16\n\x0estartup_script\x18\x01 \x01(\t\x12\x14\n\x0cstartup_args\x18\x02 \x01(\t\"\x1e\n\nJavaConfig\x12\x10\n\x08jvm_args\x18\x01 \x03(\t\"W\n\x08GoConfig\x12\x10\n\x08work_dir\x18\x01 \x01(\t\x12\x1f\n\x17\x65nable_watching_go_path\x18\x02 \x01(\x08\x12\x18\n\x10\x65nable_debugging\x18\x03 \x01(\x08\":\n\x0c\x43ustomConfig\x12\x19\n\x11\x63ustom_entrypoint\x18\x01 \x01(\t\x12\x0f\n\x07runtime\x18\x02 \x01(\t\"t\n\x08\x43loudSQL\x12\x12\n\nmysql_host\x18\x01 \x02(\t\x12\x12\n\nmysql_port\x18\x02 \x02(\x05\x12\x12\n\nmysql_user\x18\x03 \x02(\t\x12\x16\n\x0emysql_password\x18\x04 \x02(\t\x12\x14\n\x0cmysql_socket\x18\x05 \x01(\t\"(\n\x07Library\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x0f\n\x07version\x18\x02 \x02(\t\"%\n\x07\x45nviron\x12\x0b\n\x03key\x18\x01 \x02(\x0c\x12\r\n\x05value\x18\x02 \x02(\x0c\":\n\x08VMConfig\x12\x19\n\x11\x64ocker_daemon_url\x18\x01 \x01(\t\x12\x13\n\x0b\x65nable_logs\x18\x03 \x01(\x08\x42\x32\n,com.google.appengine.tools.development.proto \x02P\x01')
)
@@ -283,6 +283,13 @@
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='php_version', full_name='apphosting.tools.devappserver2.PhpConfig.php_version', index=4,
+ number=6, type=12, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b(""),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
],
extensions=[
],
@@ -295,8 +302,8 @@
extension_ranges=[],
oneofs=[
],
- serialized_start=1096,
- serialized_end=1220,
+ serialized_start=1097,
+ serialized_end=1242,
)
@@ -326,8 +333,8 @@
extension_ranges=[],
oneofs=[
],
- serialized_start=1222,
- serialized_end=1264,
+ serialized_start=1244,
+ serialized_end=1286,
)
@@ -364,8 +371,8 @@
extension_ranges=[],
oneofs=[
],
- serialized_start=1266,
- serialized_end=1326,
+ serialized_start=1288,
+ serialized_end=1348,
)
@@ -395,8 +402,8 @@
extension_ranges=[],
oneofs=[
],
- serialized_start=1328,
- serialized_end=1358,
+ serialized_start=1350,
+ serialized_end=1380,
)
@@ -440,8 +447,8 @@
extension_ranges=[],
oneofs=[
],
- serialized_start=1360,
- serialized_end=1447,
+ serialized_start=1382,
+ serialized_end=1469,
)
@@ -478,8 +485,8 @@
extension_ranges=[],
oneofs=[
],
- serialized_start=1449,
- serialized_end=1507,
+ serialized_start=1471,
+ serialized_end=1529,
)
@@ -537,8 +544,8 @@
extension_ranges=[],
oneofs=[
],
- serialized_start=1509,
- serialized_end=1625,
+ serialized_start=1531,
+ serialized_end=1647,
)
@@ -575,8 +582,8 @@
extension_ranges=[],
oneofs=[
],
- serialized_start=1627,
- serialized_end=1667,
+ serialized_start=1649,
+ serialized_end=1689,
)
@@ -613,8 +620,8 @@
extension_ranges=[],
oneofs=[
],
- serialized_start=1669,
- serialized_end=1706,
+ serialized_start=1691,
+ serialized_end=1728,
)
@@ -651,8 +658,8 @@
extension_ranges=[],
oneofs=[
],
- serialized_start=1708,
- serialized_end=1766,
+ serialized_start=1730,
+ serialized_end=1788,
)
_CONFIG.fields_by_name['libraries'].message_type = _LIBRARY
diff -u -r 1.9.67/google_appengine/google/appengine/tools/devappserver2/runtime_factories.py 1.9.68/google_appengine/google/appengine/tools/devappserver2/runtime_factories.py
--- 1.9.67/google_appengine/google/appengine/tools/devappserver2/runtime_factories.py 2018-03-09 20:05:17.000000000 -0800
+++ 1.9.68/google_appengine/google/appengine/tools/devappserver2/runtime_factories.py 2018-04-03 21:35:03.000000000 -0700
@@ -45,6 +45,7 @@
'php55': php_factory.PHPRuntimeInstanceFactory,
+ 'php72': php_factory.PHPRuntimeInstanceFactory,
'python': python_factory.PythonRuntimeInstanceFactory,
'python27': python_factory.PythonRuntimeInstanceFactory,
'python-compat': python_factory.PythonRuntimeInstanceFactory,
diff -u -r 1.9.67/google_appengine/google/appengine/tools/devappserver2/shutdown.py 1.9.68/google_appengine/google/appengine/tools/devappserver2/shutdown.py
--- 1.9.67/google_appengine/google/appengine/tools/devappserver2/shutdown.py 2018-03-09 20:05:17.000000000 -0800
+++ 1.9.68/google_appengine/google/appengine/tools/devappserver2/shutdown.py 2018-04-03 21:35:03.000000000 -0700
@@ -36,12 +36,17 @@
global _shutting_down
_shutting_down = True
+# A list of callback methods that need to be triggerd before shutdown.
+extra_shutdown_callback = []
+
def _async_terminate(*_):
async_quit()
global _num_terminate_requests
_num_terminate_requests += 1
if _num_terminate_requests == 1:
+ for call in extra_shutdown_callback:
+ call()
logging.info('Shutting down.')
if _num_terminate_requests >= 3:
logging.error('Received third interrupt signal. Terminating.')
@@ -58,7 +63,11 @@
def install_signal_handlers():
- """Installs a signal handler to do orderly shutdown."""
+ """Installs a signal handler to do orderly shutdown.
+
+ This should be called exactly once by the root process of a process hierachy.
+ Currently this root process is either dev_appserver or api_server.
+ """
signal.signal(signal.SIGTERM, _async_terminate)
signal.signal(signal.SIGINT, _async_terminate)
diff -u -r 1.9.67/google_appengine/google/appengine/tools/devappserver2/util.py 1.9.68/google_appengine/google/appengine/tools/devappserver2/util.py
--- 1.9.67/google_appengine/google/appengine/tools/devappserver2/util.py 2018-03-09 20:05:17.000000000 -0800
+++ 1.9.68/google_appengine/google/appengine/tools/devappserver2/util.py 2018-04-03 21:35:03.000000000 -0700
@@ -19,7 +19,6 @@
-import abc
import BaseHTTPServer
import os
import socket
@@ -107,18 +106,3 @@
app_id: The id of the application.
"""
os.environ['APPLICATION_ID'] = app_id
-
-
-class GcdEmulatorManager(object):
- """An abstract class defining the interfaces of Gcd Emulator Managing.
-
- Api server is agnostic of the emulator. The implementation of this class
- should be instantiated in a wrapper of api_server with direct access to the
- emulator.
- """
- __metaclass__ = abc.ABCMeta
-
- @abc.abstractmethod
- def launch(self):
- """Launch the emulator."""
- raise NotImplementedError()
diff -u -r 1.9.67/google_appengine/lib/pyasn1/pyasn1/LICENSE 1.9.68/google_appengine/lib/pyasn1/pyasn1/LICENSE
--- 1.9.67/google_appengine/lib/pyasn1/pyasn1/LICENSE 2018-03-09 20:05:30.000000000 -0800
+++ 1.9.68/google_appengine/lib/pyasn1/pyasn1/LICENSE 2018-04-03 21:35:14.000000000 -0700
@@ -1,7 +1,5 @@
-Copyright (c) 2005-2012 Ilya Etingof <[email protected]>, all rights reserved.
-
-THIS SOFTWARE IS NOT FAULT TOLERANT AND SHOULD NOT BE USED IN ANY SITUATION
-ENDANGERING HUMAN LIFE OR PROPERTY.
+Copyright (c) 2005-2017, Ilya Etingof <[email protected]>
+All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@@ -13,16 +11,14 @@
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
- * The name of the authors may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff -u -r 1.9.67/google_appengine/lib/pyasn1/pyasn1/__init__.py 1.9.68/google_appengine/lib/pyasn1/pyasn1/__init__.py
--- 1.9.67/google_appengine/lib/pyasn1/pyasn1/__init__.py 2018-03-09 20:05:30.000000000 -0800
+++ 1.9.68/google_appengine/lib/pyasn1/pyasn1/__init__.py 2018-04-03 21:35:14.000000000 -0700
@@ -1,8 +1,7 @@
import sys
# http://www.python.org/dev/peps/pep-0396/
-__version__ = '0.1.9'
+__version__ = '0.4.2'
if sys.version_info[:2] < (2, 4):
raise RuntimeError('PyASN1 requires Python 2.4 or later')
-
diff -u -r 1.9.67/google_appengine/lib/pyasn1/pyasn1/codec/ber/decoder.py 1.9.68/google_appengine/lib/pyasn1/pyasn1/codec/ber/decoder.py
--- 1.9.67/google_appengine/lib/pyasn1/pyasn1/codec/ber/decoder.py 2018-03-09 20:05:30.000000000 -0800
+++ 1.9.68/google_appengine/lib/pyasn1/pyasn1/codec/ber/decoder.py 2018-04-03 21:35:14.000000000 -0700
@@ -1,226 +1,326 @@
-# BER decoder
-from pyasn1.type import tag, univ, char, useful, tagmap
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2017, Ilya Etingof <[email protected]>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+from pyasn1 import debug
+from pyasn1 import error
from pyasn1.codec.ber import eoo
-from pyasn1.compat.octets import oct2int, isOctetsType
-from pyasn1 import debug, error
+from pyasn1.compat.integer import from_bytes
+from pyasn1.compat.octets import oct2int, octs2ints, ints2octs, null
+from pyasn1.type import base
+from pyasn1.type import char
+from pyasn1.type import tag
+from pyasn1.type import tagmap
+from pyasn1.type import univ
+from pyasn1.type import useful
-class AbstractDecoder:
+__all__ = ['decode']
+
+noValue = base.noValue
+
+
+class AbstractDecoder(object):
protoComponent = None
- def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
- length, state, decodeFun, substrateFun):
+
+ def valueDecoder(self, substrate, asn1Spec,
+ tagSet=None, length=None, state=None,
+ decodeFun=None, substrateFun=None,
+ **options):
raise error.PyAsn1Error('Decoder not implemented for %s' % (tagSet,))
- def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
- length, state, decodeFun, substrateFun):
+ def indefLenValueDecoder(self, substrate, asn1Spec,
+ tagSet=None, length=None, state=None,
+ decodeFun=None, substrateFun=None,
+ **options):
raise error.PyAsn1Error('Indefinite length mode decoder not implemented for %s' % (tagSet,))
+
class AbstractSimpleDecoder(AbstractDecoder):
- tagFormats = (tag.tagFormatSimple,)
- def _createComponent(self, asn1Spec, tagSet, value=None):
- if tagSet[0][1] not in self.tagFormats:
- raise error.PyAsn1Error('Invalid tag format %s for %s' % (tagSet[0], self.protoComponent.prettyPrintType()))
- if asn1Spec is None:
- return self.protoComponent.clone(value, tagSet)
- elif value is None:
+ @staticmethod
+ def substrateCollector(asn1Object, substrate, length):
+ return substrate[:length], substrate[length:]
+
+ def _createComponent(self, asn1Spec, tagSet, value, **options):
+ if options.get('native'):
+ return value
+ elif asn1Spec is None:
+ return self.protoComponent.clone(value, tagSet=tagSet)
+ elif value is noValue:
return asn1Spec
else:
return asn1Spec.clone(value)
-
-class AbstractConstructedDecoder(AbstractDecoder):
- tagFormats = (tag.tagFormatConstructed,)
- def _createComponent(self, asn1Spec, tagSet, value=None):
- if tagSet[0][1] not in self.tagFormats:
- raise error.PyAsn1Error('Invalid tag format %s for %s' % (tagSet[0], self.protoComponent.prettyPrintType()))
- if asn1Spec is None:
- return self.protoComponent.clone(tagSet)
- else:
- return asn1Spec.clone()
-
+
+
class ExplicitTagDecoder(AbstractSimpleDecoder):
protoComponent = univ.Any('')
- tagFormats = (tag.tagFormatConstructed,)
- def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
- length, state, decodeFun, substrateFun):
+
+ def valueDecoder(self, substrate, asn1Spec,
+ tagSet=None, length=None, state=None,
+ decodeFun=None, substrateFun=None,
+ **options):
if substrateFun:
return substrateFun(
- self._createComponent(asn1Spec, tagSet, ''),
- substrate, length
- )
+ self._createComponent(asn1Spec, tagSet, '', **options),
+ substrate, length
+ )
+
head, tail = substrate[:length], substrate[length:]
- value, _ = decodeFun(head, asn1Spec, tagSet, length)
+
+ value, _ = decodeFun(head, asn1Spec, tagSet, length, **options)
+
return value, tail
- def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
- length, state, decodeFun, substrateFun):
+ def indefLenValueDecoder(self, substrate, asn1Spec,
+ tagSet=None, length=None, state=None,
+ decodeFun=None, substrateFun=None,
+ **options):
if substrateFun:
return substrateFun(
- self._createComponent(asn1Spec, tagSet, ''),
- substrate, length
- )
- value, substrate = decodeFun(substrate, asn1Spec, tagSet, length)
- terminator, substrate = decodeFun(substrate, allowEoo=True)
- if eoo.endOfOctets.isSameTypeWith(terminator) and \
- terminator == eoo.endOfOctets:
+ self._createComponent(asn1Spec, tagSet, '', **options),
+ substrate, length
+ )
+
+ value, substrate = decodeFun(substrate, asn1Spec, tagSet, length, **options)
+
+ eooMarker, substrate = decodeFun(substrate, allowEoo=True, **options)
+
+ if eooMarker is eoo.endOfOctets:
return value, substrate
else:
raise error.PyAsn1Error('Missing end-of-octets terminator')
+
explicitTagDecoder = ExplicitTagDecoder()
+
class IntegerDecoder(AbstractSimpleDecoder):
protoComponent = univ.Integer(0)
- precomputedValues = {
- '\x00': 0,
- '\x01': 1,
- '\x02': 2,
- '\x03': 3,
- '\x04': 4,
- '\x05': 5,
- '\x06': 6,
- '\x07': 7,
- '\x08': 8,
- '\x09': 9,
- '\xff': -1,
- '\xfe': -2,
- '\xfd': -3,
- '\xfc': -4,
- '\xfb': -5
- }
-
- def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length,
- state, decodeFun, substrateFun):
+
+ def valueDecoder(self, substrate, asn1Spec,
+ tagSet=None, length=None, state=None,
+ decodeFun=None, substrateFun=None,
+ **options):
+
+ if tagSet[0].tagFormat != tag.tagFormatSimple:
+ raise error.PyAsn1Error('Simple tag format expected')
+
head, tail = substrate[:length], substrate[length:]
+
if not head:
- return self._createComponent(asn1Spec, tagSet, 0), tail
- if head in self.precomputedValues:
- value = self.precomputedValues[head]
- else:
- firstOctet = oct2int(head[0])
- if firstOctet & 0x80:
- value = -1
- else:
- value = 0
- for octet in head:
- value = value << 8 | oct2int(octet)
- return self._createComponent(asn1Spec, tagSet, value), tail
+ return self._createComponent(asn1Spec, tagSet, 0, **options), tail
+
+ value = from_bytes(head, signed=True)
+
+ return self._createComponent(asn1Spec, tagSet, value, **options), tail
+
class BooleanDecoder(IntegerDecoder):
protoComponent = univ.Boolean(0)
- def _createComponent(self, asn1Spec, tagSet, value=None):
- return IntegerDecoder._createComponent(self, asn1Spec, tagSet, value and 1 or 0)
+
+ def _createComponent(self, asn1Spec, tagSet, value, **options):
+ return IntegerDecoder._createComponent(self, asn1Spec, tagSet, value and 1 or 0, **options)
+
class BitStringDecoder(AbstractSimpleDecoder):
protoComponent = univ.BitString(())
- tagFormats = (tag.tagFormatSimple, tag.tagFormatConstructed)
- def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length,
- state, decodeFun, substrateFun):
+ supportConstructedForm = True
+
+ def valueDecoder(self, substrate, asn1Spec,
+ tagSet=None, length=None, state=None,
+ decodeFun=None, substrateFun=None,
+ **options):
head, tail = substrate[:length], substrate[length:]
- if tagSet[0][1] == tag.tagFormatSimple: # XXX what tag to check?
- if not head:
- raise error.PyAsn1Error('Empty substrate')
+
+ if substrateFun:
+ return substrateFun(self._createComponent(asn1Spec, tagSet, noValue, **options),
+ substrate, length)
+
+ if not head:
+ raise error.PyAsn1Error('Empty BIT STRING substrate')
+
+ if tagSet[0].tagFormat == tag.tagFormatSimple: # XXX what tag to check?
+
trailingBits = oct2int(head[0])
if trailingBits > 7:
raise error.PyAsn1Error(
'Trailing bits overflow %s' % trailingBits
- )
- head = head[1:]
- lsb = p = 0; l = len(head)-1; b = []
- while p <= l:
- if p == l:
- lsb = trailingBits
- j = 7
- o = oct2int(head[p])
- while j >= lsb:
- b.append((o>>j)&0x01)
- j = j - 1
- p = p + 1
- return self._createComponent(asn1Spec, tagSet, b), tail
- r = self._createComponent(asn1Spec, tagSet, ())
- if substrateFun:
- return substrateFun(r, substrate, length)
+ )
+
+ value = self.protoComponent.fromOctetString(head[1:], internalFormat=True, padding=trailingBits)
+
+ return self._createComponent(asn1Spec, tagSet, value, **options), tail
+
+ if not self.supportConstructedForm:
+ raise error.PyAsn1Error('Constructed encoding form prohibited at %s' % self.__class__.__name__)
+
+ # All inner fragments are of the same type, treat them as octet string
+ substrateFun = self.substrateCollector
+
+ bitString = self.protoComponent.fromOctetString(null, internalFormat=True)
+
while head:
- component, head = decodeFun(head, self.protoComponent)
- r = r + component
- return r, tail
-
- def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
- length, state, decodeFun, substrateFun):
- r = self._createComponent(asn1Spec, tagSet, '')
+ component, head = decodeFun(head, self.protoComponent,
+ substrateFun=substrateFun, **options)
+
+ trailingBits = oct2int(component[0])
+ if trailingBits > 7:
+ raise error.PyAsn1Error(
+ 'Trailing bits overflow %s' % trailingBits
+ )
+
+ bitString = self.protoComponent.fromOctetString(
+ component[1:], internalFormat=True,
+ prepend=bitString, padding=trailingBits
+ )
+
+ return self._createComponent(asn1Spec, tagSet, bitString, **options), tail
+
+ def indefLenValueDecoder(self, substrate, asn1Spec,
+ tagSet=None, length=None, state=None,
+ decodeFun=None, substrateFun=None,
+ **options):
+
if substrateFun:
- return substrateFun(r, substrate, length)
+ return substrateFun(self._createComponent(asn1Spec, tagSet, noValue, **options), substrate, length)
+
+ # All inner fragments are of the same type, treat them as octet string
+ substrateFun = self.substrateCollector
+
+ bitString = self.protoComponent.fromOctetString(null, internalFormat=True)
+
while substrate:
component, substrate = decodeFun(substrate, self.protoComponent,
- allowEoo=True)
- if eoo.endOfOctets.isSameTypeWith(component) and \
- component == eoo.endOfOctets:
+ substrateFun=substrateFun,
+ allowEoo=True, **options)
+ if component is eoo.endOfOctets:
break
- r = r + component
- else:
- raise error.SubstrateUnderrunError(
- 'No EOO seen before substrate ends'
+
+ trailingBits = oct2int(component[0])
+ if trailingBits > 7:
+ raise error.PyAsn1Error(
+ 'Trailing bits overflow %s' % trailingBits
)
- return r, substrate
+
+ bitString = self.protoComponent.fromOctetString(
+ component[1:], internalFormat=True,
+ prepend=bitString, padding=trailingBits
+ )
+
+ else:
+ raise error.SubstrateUnderrunError('No EOO seen before substrate ends')
+
+ return self._createComponent(asn1Spec, tagSet, bitString, **options), substrate
+
class OctetStringDecoder(AbstractSimpleDecoder):
protoComponent = univ.OctetString('')
- tagFormats = (tag.tagFormatSimple, tag.tagFormatConstructed)
- def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length,
- state, decodeFun, substrateFun):
+ supportConstructedForm = True
+
+ def valueDecoder(self, substrate, asn1Spec,
+ tagSet=None, length=None, state=None,
+ decodeFun=None, substrateFun=None,
+ **options):
head, tail = substrate[:length], substrate[length:]
- if tagSet[0][1] == tag.tagFormatSimple: # XXX what tag to check?
- return self._createComponent(asn1Spec, tagSet, head), tail
- r = self._createComponent(asn1Spec, tagSet, '')
+
if substrateFun:
- return substrateFun(r, substrate, length)
+ return substrateFun(self._createComponent(asn1Spec, tagSet, noValue, **options),
+ substrate, length)
+
+ if tagSet[0].tagFormat == tag.tagFormatSimple: # XXX what tag to check?
+ return self._createComponent(asn1Spec, tagSet, head, **options), tail
+
+ if not self.supportConstructedForm:
+ raise error.PyAsn1Error('Constructed encoding form prohibited at %s' % self.__class__.__name__)
+
+ # All inner fragments are of the same type, treat them as octet string
+ substrateFun = self.substrateCollector
+
+ header = null
+
while head:
- component, head = decodeFun(head, self.protoComponent)
- r = r + component
- return r, tail
-
- def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
- length, state, decodeFun, substrateFun):
- r = self._createComponent(asn1Spec, tagSet, '')
- if substrateFun:
- return substrateFun(r, substrate, length)
+ component, head = decodeFun(head, self.protoComponent,
+ substrateFun=substrateFun,
+ **options)
+ header += component
+
+ return self._createComponent(asn1Spec, tagSet, header, **options), tail
+
+ def indefLenValueDecoder(self, substrate, asn1Spec,
+ tagSet=None, length=None, state=None,
+ decodeFun=None, substrateFun=None,
+ **options):
+ if substrateFun and substrateFun is not self.substrateCollector:
+ asn1Object = self._createComponent(asn1Spec, tagSet, noValue, **options)
+ return substrateFun(asn1Object, substrate, length)
+
+ # All inner fragments are of the same type, treat them as octet string
+ substrateFun = self.substrateCollector
+
+ header = null
+
while substrate:
- component, substrate = decodeFun(substrate, self.protoComponent,
- allowEoo=True)
- if eoo.endOfOctets.isSameTypeWith(component) and \
- component == eoo.endOfOctets:
+ component, substrate = decodeFun(substrate,
+ self.protoComponent,
+ substrateFun=substrateFun,
+ allowEoo=True, **options)
+ if component is eoo.endOfOctets:
break
- r = r + component
+ header += component
else:
raise error.SubstrateUnderrunError(
'No EOO seen before substrate ends'
- )
- return r, substrate
+ )
+
+ return self._createComponent(asn1Spec, tagSet, header, **options), substrate
+
class NullDecoder(AbstractSimpleDecoder):
protoComponent = univ.Null('')
- def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
- length, state, decodeFun, substrateFun):
+
+ def valueDecoder(self, substrate, asn1Spec,
+ tagSet=None, length=None, state=None,
+ decodeFun=None, substrateFun=None,
+ **options):
+
+ if tagSet[0].tagFormat != tag.tagFormatSimple:
+ raise error.PyAsn1Error('Simple tag format expected')
+
head, tail = substrate[:length], substrate[length:]
- r = self._createComponent(asn1Spec, tagSet)
+
+ component = self._createComponent(asn1Spec, tagSet, '', **options)
+
if head:
raise error.PyAsn1Error('Unexpected %d-octet substrate for Null' % length)
- return r, tail
+
+ return component, tail
+
class ObjectIdentifierDecoder(AbstractSimpleDecoder):
protoComponent = univ.ObjectIdentifier(())
- def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length,
- state, decodeFun, substrateFun):
+
+ def valueDecoder(self, substrate, asn1Spec,
+ tagSet=None, length=None, state=None,
+ decodeFun=None, substrateFun=None,
+ **options):
+ if tagSet[0].tagFormat != tag.tagFormatSimple:
+ raise error.PyAsn1Error('Simple tag format expected')
+
head, tail = substrate[:length], substrate[length:]
if not head:
raise error.PyAsn1Error('Empty substrate')
+ head = octs2ints(head)
+
oid = ()
index = 0
substrateLen = len(head)
while index < substrateLen:
- subId = oct2int(head[index])
+ subId = head[index]
index += 1
if subId < 128:
- oid = oid + (subId,)
+ oid += (subId,)
elif subId > 128:
# Construct subid from a number of octets
nextSubId = subId
@@ -231,36 +331,46 @@
raise error.SubstrateUnderrunError(
'Short substrate for sub-OID past %s' % (oid,)
)
- nextSubId = oct2int(head[index])
+ nextSubId = head[index]
index += 1
- oid = oid + ((subId << 7) + nextSubId,)
+ oid += ((subId << 7) + nextSubId,)
elif subId == 128:
# ASN.1 spec forbids leading zeros (0x80) in OID
# encoding, tolerating it opens a vulnerability. See
# http://www.cosic.esat.kuleuven.be/publications/article-1432.pdf
# page 7
raise error.PyAsn1Error('Invalid octet 0x80 in OID encoding')
-
+
# Decode two leading arcs
if 0 <= oid[0] <= 39:
oid = (0,) + oid
elif 40 <= oid[0] <= 79:
- oid = (1, oid[0]-40) + oid[1:]
+ oid = (1, oid[0] - 40) + oid[1:]
elif oid[0] >= 80:
- oid = (2, oid[0]-80) + oid[1:]
+ oid = (2, oid[0] - 80) + oid[1:]
else:
raise error.PyAsn1Error('Malformed first OID octet: %s' % head[0])
- return self._createComponent(asn1Spec, tagSet, oid), tail
+ return self._createComponent(asn1Spec, tagSet, oid, **options), tail
+
class RealDecoder(AbstractSimpleDecoder):
protoComponent = univ.Real()
- def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
- length, state, decodeFun, substrateFun):
+
+ def valueDecoder(self, substrate, asn1Spec,
+ tagSet=None, length=None, state=None,
+ decodeFun=None, substrateFun=None,
+ **options):
+ if tagSet[0].tagFormat != tag.tagFormatSimple:
+ raise error.PyAsn1Error('Simple tag format expected')
+
head, tail = substrate[:length], substrate[length:]
+
if not head:
- return self._createComponent(asn1Spec, tagSet, 0.0), tail
- fo = oct2int(head[0]); head = head[1:]
+ return self._createComponent(asn1Spec, tagSet, 0.0, **options), tail
+
+ fo = oct2int(head[0])
+ head = head[1:]
if fo & 0x80: # binary encoding
if not head:
raise error.PyAsn1Error("Incomplete floating-point value")
@@ -272,26 +382,26 @@
if not eo or not head:
raise error.PyAsn1Error('Real exponent screwed')
e = oct2int(eo[0]) & 0x80 and -1 or 0
- while eo: # exponent
+ while eo: # exponent
e <<= 8
e |= oct2int(eo[0])
eo = eo[1:]
- b = fo >> 4 & 0x03 # base bits
+ b = fo >> 4 & 0x03 # base bits
if b > 2:
raise error.PyAsn1Error('Illegal Real base')
- if b == 1: # encbase = 8
+ if b == 1: # encbase = 8
e *= 3
- elif b == 2: # encbase = 16
+ elif b == 2: # encbase = 16
e *= 4
p = 0
while head: # value
p <<= 8
p |= oct2int(head[0])
head = head[1:]
- if fo & 0x40: # sign bit
+ if fo & 0x40: # sign bit
p = -p
sf = fo >> 2 & 0x03 # scale bits
- p *= 2**sf
+ p *= 2 ** sf
value = (p, 2, e)
elif fo & 0x40: # infinite value
value = fo & 0x01 and '-inf' or 'inf'
@@ -308,248 +418,571 @@
else:
raise error.SubstrateUnderrunError(
'Unknown NR (tag %s)' % fo
- )
+ )
except ValueError:
raise error.SubstrateUnderrunError(
'Bad character Real syntax'
- )
+ )
else:
raise error.SubstrateUnderrunError(
'Unknown encoding (tag %s)' % fo
- )
- return self._createComponent(asn1Spec, tagSet, value), tail
-
-class SequenceDecoder(AbstractConstructedDecoder):
- protoComponent = univ.Sequence()
- def _getComponentTagMap(self, r, idx):
- try:
- return r.getComponentTagMapNearPosition(idx)
- except error.PyAsn1Error:
- return
-
- def _getComponentPositionByType(self, r, t, idx):
- return r.getComponentPositionNearType(t, idx)
-
- def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
- length, state, decodeFun, substrateFun):
- head, tail = substrate[:length], substrate[length:]
- r = self._createComponent(asn1Spec, tagSet)
- idx = 0
- if substrateFun:
- return substrateFun(r, substrate, length)
- while head:
- asn1Spec = self._getComponentTagMap(r, idx)
- component, head = decodeFun(head, asn1Spec)
- idx = self._getComponentPositionByType(
- r, component.getEffectiveTagSet(), idx
- )
- r.setComponentByPosition(idx, component, asn1Spec is None)
- idx = idx + 1
- r.setDefaultComponents()
- r.verifySizeSpec()
- return r, tail
-
- def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
- length, state, decodeFun, substrateFun):
- r = self._createComponent(asn1Spec, tagSet)
- if substrateFun:
- return substrateFun(r, substrate, length)
- idx = 0
+ )
+ return self._createComponent(asn1Spec, tagSet, value, **options), tail
+
+
+class AbstractConstructedDecoder(AbstractDecoder):
+ protoComponent = None
+
+
+class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
+ protoRecordComponent = None
+ protoSequenceComponent = None
+
+ def _getComponentTagMap(self, asn1Object, idx):
+ raise NotImplementedError()
+
+ def _getComponentPositionByType(self, asn1Object, tagSet, idx):
+ raise NotImplementedError()
+
+ def _decodeComponents(self, substrate, tagSet=None, decodeFun=None, **options):
+ components = []
+ componentTypes = set()
while substrate:
- asn1Spec = self._getComponentTagMap(r, idx)
- component, substrate = decodeFun(substrate, asn1Spec, allowEoo=True)
- if eoo.endOfOctets.isSameTypeWith(component) and \
- component == eoo.endOfOctets:
+ component, substrate = decodeFun(substrate, **options)
+ if component is eoo.endOfOctets:
break
- idx = self._getComponentPositionByType(
- r, component.getEffectiveTagSet(), idx
- )
- r.setComponentByPosition(idx, component, asn1Spec is None)
- idx = idx + 1
+ components.append(component)
+ componentTypes.add(component.tagSet)
+
+ # Now we have to guess is it SEQUENCE/SET or SEQUENCE OF/SET OF
+ # The heuristics is:
+ # * 1+ components of different types -> likely SEQUENCE/SET
+ # * otherwise -> likely SEQUENCE OF/SET OF
+ if len(componentTypes) > 1:
+ protoComponent = self.protoRecordComponent
else:
- raise error.SubstrateUnderrunError(
- 'No EOO seen before substrate ends'
- )
- r.setDefaultComponents()
- r.verifySizeSpec()
- return r, substrate
-
-class SequenceOfDecoder(AbstractConstructedDecoder):
- protoComponent = univ.SequenceOf()
- def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
- length, state, decodeFun, substrateFun):
+ protoComponent = self.protoSequenceComponent
+
+ asn1Object = protoComponent.clone(
+ # construct tagSet from base tag from prototype ASN.1 object
+ # and additional tags recovered from the substrate
+ tagSet=tag.TagSet(protoComponent.tagSet.baseTag, *tagSet.superTags)
+ )
+
+ for idx, component in enumerate(components):
+ asn1Object.setComponentByPosition(
+ idx, component,
+ verifyConstraints=False,
+ matchTags=False, matchConstraints=False
+ )
+
+ return asn1Object, substrate
+
+ def valueDecoder(self, substrate, asn1Spec,
+ tagSet=None, length=None, state=None,
+ decodeFun=None, substrateFun=None,
+ **options):
+ if tagSet[0].tagFormat != tag.tagFormatConstructed:
+ raise error.PyAsn1Error('Constructed tag format expected')
+
head, tail = substrate[:length], substrate[length:]
- r = self._createComponent(asn1Spec, tagSet)
- if substrateFun:
- return substrateFun(r, substrate, length)
- asn1Spec = r.getComponentType()
- idx = 0
- while head:
- component, head = decodeFun(head, asn1Spec)
- r.setComponentByPosition(idx, component, asn1Spec is None)
- idx = idx + 1
- r.verifySizeSpec()
- return r, tail
-
- def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
- length, state, decodeFun, substrateFun):
- r = self._createComponent(asn1Spec, tagSet)
- if substrateFun:
- return substrateFun(r, substrate, length)
- asn1Spec = r.getComponentType()
- idx = 0
- while substrate:
- component, substrate = decodeFun(substrate, asn1Spec, allowEoo=True)
- if eoo.endOfOctets.isSameTypeWith(component) and \
- component == eoo.endOfOctets:
- break
- r.setComponentByPosition(idx, component, asn1Spec is None)
- idx = idx + 1
+
+ if substrateFun is not None:
+ if asn1Spec is not None:
+ asn1Object = asn1Spec.clone()
+ elif self.protoComponent is not None:
+ asn1Object = self.protoComponent.clone(tagSet=tagSet)
+ else:
+ asn1Object = self.protoRecordComponent, self.protoSequenceComponent
+
+ return substrateFun(asn1Object, substrate, length)
+
+ if asn1Spec is None:
+ asn1Object, trailing = self._decodeComponents(
+ head, tagSet=tagSet, decodeFun=decodeFun, **options
+ )
+ if trailing:
+ raise error.PyAsn1Error('Unused trailing %d octets encountered' % len(trailing))
+ return asn1Object, tail
+
+ asn1Object = asn1Spec.clone()
+
+ if asn1Spec.typeId in (univ.Sequence.typeId, univ.Set.typeId):
+
+ namedTypes = asn1Spec.componentType
+
+ isSetType = asn1Spec.typeId == univ.Set.typeId
+ isDeterministic = not isSetType and not namedTypes.hasOptionalOrDefault
+
+ seenIndices = set()
+ idx = 0
+ while head:
+ if not namedTypes:
+ componentType = None
+ elif isSetType:
+ componentType = namedTypes.tagMapUnique
+ else:
+ try:
+ if isDeterministic:
+ componentType = namedTypes[idx].asn1Object
+ elif namedTypes[idx].isOptional or namedTypes[idx].isDefaulted:
+ componentType = namedTypes.getTagMapNearPosition(idx)
+ else:
+ componentType = namedTypes[idx].asn1Object
+ except IndexError:
+ raise error.PyAsn1Error(
+ 'Excessive components decoded at %r' % (asn1Spec,)
+ )
+
+ component, head = decodeFun(head, componentType, **options)
+
+ if not isDeterministic and namedTypes:
+ if isSetType:
+ idx = namedTypes.getPositionByType(component.effectiveTagSet)
+ elif namedTypes[idx].isOptional or namedTypes[idx].isDefaulted:
+ idx = namedTypes.getPositionNearType(component.effectiveTagSet, idx)
+
+ asn1Object.setComponentByPosition(
+ idx, component,
+ verifyConstraints=False,
+ matchTags=False, matchConstraints=False
+ )
+
+ seenIndices.add(idx)
+ idx += 1
+
+ if namedTypes:
+ if not namedTypes.requiredComponents.issubset(seenIndices):
+ raise error.PyAsn1Error('ASN.1 object %s has uninitialized components' % asn1Object.__class__.__name__)
+
+ if namedTypes.hasOpenTypes:
+
+ openTypes = options.get('openTypes', {})
+
+ if openTypes or options.get('decodeOpenTypes', False):
+
+ for idx, namedType in enumerate(namedTypes.namedTypes):
+ if not namedType.openType:
+ continue
+
+ if namedType.isOptional and not asn1Object.getComponentByPosition(idx).isValue:
+ continue
+
+ governingValue = asn1Object.getComponentByName(
+ namedType.openType.name
+ )
+
+ try:
+ openType = openTypes[governingValue]
+
+ except KeyError:
+
+ try:
+ openType = namedType.openType[governingValue]
+
+ except KeyError:
+ continue
+
+ component, rest = decodeFun(
+ asn1Object.getComponentByPosition(idx).asOctets(),
+ asn1Spec=openType
+ )
+
+ asn1Object.setComponentByPosition(idx, component)
+
+ else:
+ asn1Object.verifySizeSpec()
+
else:
- raise error.SubstrateUnderrunError(
- 'No EOO seen before substrate ends'
+ asn1Object = asn1Spec.clone()
+
+ componentType = asn1Spec.componentType
+
+ idx = 0
+
+ while head:
+ component, head = decodeFun(head, componentType, **options)
+ asn1Object.setComponentByPosition(
+ idx, component,
+ verifyConstraints=False,
+ matchTags=False, matchConstraints=False
+ )
+ idx += 1
+
+ return asn1Object, tail
+
+ def indefLenValueDecoder(self, substrate, asn1Spec,
+ tagSet=None, length=None, state=None,
+ decodeFun=None, substrateFun=None,
+ **options):
+ if tagSet[0].tagFormat != tag.tagFormatConstructed:
+ raise error.PyAsn1Error('Constructed tag format expected')
+
+ if substrateFun is not None:
+ if asn1Spec is not None:
+ asn1Object = asn1Spec.clone()
+ elif self.protoComponent is not None:
+ asn1Object = self.protoComponent.clone(tagSet=tagSet)
+ else:
+ asn1Object = self.protoRecordComponent, self.protoSequenceComponent
+
+ return substrateFun(asn1Object, substrate, length)
+
+ if asn1Spec is None:
+ return self._decodeComponents(
+ substrate, tagSet=tagSet, decodeFun=decodeFun, allowEoo=True, **options
+ )
+
+ asn1Object = asn1Spec.clone()
+
+ if asn1Spec.typeId in (univ.Sequence.typeId, univ.Set.typeId):
+
+ namedTypes = asn1Object.componentType
+
+ isSetType = asn1Object.typeId == univ.Set.typeId
+ isDeterministic = not isSetType and not namedTypes.hasOptionalOrDefault
+
+ seenIndices = set()
+ idx = 0
+ while substrate:
+ if len(namedTypes) <= idx:
+ asn1Spec = None
+ elif isSetType:
+ asn1Spec = namedTypes.tagMapUnique
+ else:
+ try:
+ if isDeterministic:
+ asn1Spec = namedTypes[idx].asn1Object
+ elif namedTypes[idx].isOptional or namedTypes[idx].isDefaulted:
+ asn1Spec = namedTypes.getTagMapNearPosition(idx)
+ else:
+ asn1Spec = namedTypes[idx].asn1Object
+ except IndexError:
+ raise error.PyAsn1Error(
+ 'Excessive components decoded at %r' % (asn1Object,)
+ )
+
+ component, substrate = decodeFun(substrate, asn1Spec, allowEoo=True, **options)
+ if component is eoo.endOfOctets:
+ break
+
+ if not isDeterministic and namedTypes:
+ if isSetType:
+ idx = namedTypes.getPositionByType(component.effectiveTagSet)
+ elif namedTypes[idx].isOptional or namedTypes[idx].isDefaulted:
+ idx = namedTypes.getPositionNearType(component.effectiveTagSet, idx)
+
+ asn1Object.setComponentByPosition(
+ idx, component,
+ verifyConstraints=False,
+ matchTags=False, matchConstraints=False
+ )
+
+ seenIndices.add(idx)
+ idx += 1
+
+ else:
+ raise error.SubstrateUnderrunError(
+ 'No EOO seen before substrate ends'
)
- r.verifySizeSpec()
- return r, substrate
-class SetDecoder(SequenceDecoder):
+ if namedTypes:
+ if not namedTypes.requiredComponents.issubset(seenIndices):
+ raise error.PyAsn1Error('ASN.1 object %s has uninitialized components' % asn1Object.__class__.__name__)
+
+ if namedTypes.hasOpenTypes:
+
+ openTypes = options.get('openTypes', None)
+
+ if openTypes or options.get('decodeOpenTypes', False):
+
+ for idx, namedType in enumerate(namedTypes.namedTypes):
+ if not namedType.openType:
+ continue
+
+ if namedType.isOptional and not asn1Object.getComponentByPosition(idx).isValue:
+ continue
+
+ governingValue = asn1Object.getComponentByName(
+ namedType.openType.name
+ )
+
+ try:
+ openType = openTypes[governingValue]
+
+ except KeyError:
+
+ try:
+ openType = namedType.openType[governingValue]
+
+ except KeyError:
+ continue
+
+ component, rest = decodeFun(
+ asn1Object.getComponentByPosition(idx).asOctets(),
+ asn1Spec=openType, allowEoo=True
+ )
+
+ if component is not eoo.endOfOctets:
+ asn1Object.setComponentByPosition(idx, component)
+
+ else:
+ asn1Object.verifySizeSpec()
+
+ else:
+ asn1Object = asn1Spec.clone()
+
+ componentType = asn1Spec.componentType
+
+ idx = 0
+
+ while substrate:
+ component, substrate = decodeFun(substrate, componentType, allowEoo=True, **options)
+
+ if component is eoo.endOfOctets:
+ break
+
+ asn1Object.setComponentByPosition(
+ idx, component,
+ verifyConstraints=False,
+ matchTags=False, matchConstraints=False
+ )
+ idx += 1
+ else:
+ raise error.SubstrateUnderrunError(
+ 'No EOO seen before substrate ends'
+ )
+
+ return asn1Object, substrate
+
+
+class SequenceOrSequenceOfDecoder(UniversalConstructedTypeDecoder):
+ protoRecordComponent = univ.Sequence()
+ protoSequenceComponent = univ.SequenceOf()
+
+
+class SequenceDecoder(SequenceOrSequenceOfDecoder):
+ protoComponent = univ.Sequence()
+
+
+class SequenceOfDecoder(SequenceOrSequenceOfDecoder):
+ protoComponent = univ.SequenceOf()
+
+
+class SetOrSetOfDecoder(UniversalConstructedTypeDecoder):
+ protoRecordComponent = univ.Set()
+ protoSequenceComponent = univ.SetOf()
+
+
+class SetDecoder(SetOrSetOfDecoder):
protoComponent = univ.Set()
- def _getComponentTagMap(self, r, idx):
- return r.getComponentTagMap()
- def _getComponentPositionByType(self, r, t, idx):
- nextIdx = r.getComponentPositionByType(t)
- if nextIdx is None:
- return idx
- else:
- return nextIdx
-
-class SetOfDecoder(SequenceOfDecoder):
+
+
+class SetOfDecoder(SetOrSetOfDecoder):
protoComponent = univ.SetOf()
-
+
+
class ChoiceDecoder(AbstractConstructedDecoder):
protoComponent = univ.Choice()
- tagFormats = (tag.tagFormatSimple, tag.tagFormatConstructed)
- def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
- length, state, decodeFun, substrateFun):
+
+ def valueDecoder(self, substrate, asn1Spec,
+ tagSet=None, length=None, state=None,
+ decodeFun=None, substrateFun=None,
+ **options):
head, tail = substrate[:length], substrate[length:]
- r = self._createComponent(asn1Spec, tagSet)
+
+ if asn1Spec is None:
+ asn1Object = self.protoComponent.clone(tagSet=tagSet)
+ else:
+ asn1Object = asn1Spec.clone()
+
if substrateFun:
- return substrateFun(r, substrate, length)
- if r.getTagSet() == tagSet: # explicitly tagged Choice
+ return substrateFun(asn1Object, substrate, length)
+
+ if asn1Object.tagSet == tagSet: # explicitly tagged Choice
component, head = decodeFun(
- head, r.getComponentTagMap()
- )
+ head, asn1Object.componentTagMap, **options
+ )
+
else:
component, head = decodeFun(
- head, r.getComponentTagMap(), tagSet, length, state
- )
- if isinstance(component, univ.Choice):
- effectiveTagSet = component.getEffectiveTagSet()
+ head, asn1Object.componentTagMap,
+ tagSet, length, state, **options
+ )
+
+ effectiveTagSet = component.effectiveTagSet
+
+ asn1Object.setComponentByType(
+ effectiveTagSet, component,
+ verifyConstraints=False,
+ matchTags=False, matchConstraints=False,
+ innerFlag=False
+ )
+
+ return asn1Object, tail
+
+ def indefLenValueDecoder(self, substrate, asn1Spec,
+ tagSet=None, length=None, state=None,
+ decodeFun=None, substrateFun=None,
+ **options):
+ if asn1Spec is None:
+ asn1Object = self.protoComponent.clone(tagSet=tagSet)
else:
- effectiveTagSet = component.getTagSet()
- r.setComponentByType(effectiveTagSet, component, 0, asn1Spec is None)
- return r, tail
-
- def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
- length, state, decodeFun, substrateFun):
- r = self._createComponent(asn1Spec, tagSet)
+ asn1Object = asn1Spec.clone()
+
if substrateFun:
- return substrateFun(r, substrate, length)
- if r.getTagSet() == tagSet: # explicitly tagged Choice
- component, substrate = decodeFun(substrate, r.getComponentTagMap())
+ return substrateFun(asn1Object, substrate, length)
+
+ if asn1Object.tagSet == tagSet: # explicitly tagged Choice
+ component, substrate = decodeFun(
+ substrate, asn1Object.componentType.tagMapUnique, **options
+ )
# eat up EOO marker
- eooMarker, substrate = decodeFun(substrate, allowEoo=True)
- if not eoo.endOfOctets.isSameTypeWith(eooMarker) or \
- eooMarker != eoo.endOfOctets:
+ eooMarker, substrate = decodeFun(
+ substrate, allowEoo=True, **options
+ )
+ if eooMarker is not eoo.endOfOctets:
raise error.PyAsn1Error('No EOO seen before substrate ends')
+
else:
- component, substrate= decodeFun(
- substrate, r.getComponentTagMap(), tagSet, length, state
+ component, substrate = decodeFun(
+ substrate, asn1Object.componentType.tagMapUnique,
+ tagSet, length, state, **options
)
- if isinstance(component, univ.Choice):
- effectiveTagSet = component.getEffectiveTagSet()
- else:
- effectiveTagSet = component.getTagSet()
- r.setComponentByType(effectiveTagSet, component, 0, asn1Spec is None)
- return r, substrate
+
+ effectiveTagSet = component.effectiveTagSet
+
+ asn1Object.setComponentByType(
+ effectiveTagSet, component,
+ verifyConstraints=False,
+ matchTags=False, matchConstraints=False,
+ innerFlag=False
+ )
+
+ return asn1Object, substrate
+
class AnyDecoder(AbstractSimpleDecoder):
protoComponent = univ.Any()
- tagFormats = (tag.tagFormatSimple, tag.tagFormatConstructed)
- def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
- length, state, decodeFun, substrateFun):
- if asn1Spec is None or \
- asn1Spec is not None and tagSet != asn1Spec.getTagSet():
+
+ def valueDecoder(self, substrate, asn1Spec,
+ tagSet=None, length=None, state=None,
+ decodeFun=None, substrateFun=None,
+ **options):
+ if asn1Spec is None or asn1Spec is not None and tagSet != asn1Spec.tagSet:
+ fullSubstrate = options['fullSubstrate']
+
# untagged Any container, recover inner header substrate
- length = length + len(fullSubstrate) - len(substrate)
+ length += len(fullSubstrate) - len(substrate)
substrate = fullSubstrate
+
if substrateFun:
- return substrateFun(self._createComponent(asn1Spec, tagSet),
+ return substrateFun(self._createComponent(asn1Spec, tagSet, noValue, **options),
substrate, length)
+
head, tail = substrate[:length], substrate[length:]
- return self._createComponent(asn1Spec, tagSet, value=head), tail
- def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
- length, state, decodeFun, substrateFun):
- if asn1Spec is not None and tagSet == asn1Spec.getTagSet():
+ return self._createComponent(asn1Spec, tagSet, head, **options), tail
+
+ def indefLenValueDecoder(self, substrate, asn1Spec,
+ tagSet=None, length=None, state=None,
+ decodeFun=None, substrateFun=None,
+ **options):
+ if asn1Spec is not None and tagSet == asn1Spec.tagSet:
# tagged Any type -- consume header substrate
- header = ''
+ header = null
else:
+ fullSubstrate = options['fullSubstrate']
+
# untagged Any, recover header substrate
header = fullSubstrate[:-len(substrate)]
- r = self._createComponent(asn1Spec, tagSet, header)
-
# Any components do not inherit initial tag
asn1Spec = self.protoComponent
-
- if substrateFun:
- return substrateFun(r, substrate, length)
+
+ if substrateFun and substrateFun is not self.substrateCollector:
+ asn1Object = self._createComponent(asn1Spec, tagSet, noValue, **options)
+ return substrateFun(asn1Object, header + substrate, length + len(header))
+
+ # All inner fragments are of the same type, treat them as octet string
+ substrateFun = self.substrateCollector
+
while substrate:
- component, substrate = decodeFun(substrate, asn1Spec, allowEoo=True)
- if eoo.endOfOctets.isSameTypeWith(component) and \
- component == eoo.endOfOctets:
+ component, substrate = decodeFun(substrate, asn1Spec,
+ substrateFun=substrateFun,
+ allowEoo=True, **options)
+ if component is eoo.endOfOctets:
break
- r = r + component
+ header += component
else:
raise error.SubstrateUnderrunError(
'No EOO seen before substrate ends'
- )
- return r, substrate
+ )
+ if substrateFun:
+ return header, substrate
+ else:
+ return self._createComponent(asn1Spec, tagSet, header, **options), substrate
+
# character string types
class UTF8StringDecoder(OctetStringDecoder):
protoComponent = char.UTF8String()
+
+
class NumericStringDecoder(OctetStringDecoder):
protoComponent = char.NumericString()
+
+
class PrintableStringDecoder(OctetStringDecoder):
protoComponent = char.PrintableString()
+
+
class TeletexStringDecoder(OctetStringDecoder):
protoComponent = char.TeletexString()
+
+
class VideotexStringDecoder(OctetStringDecoder):
protoComponent = char.VideotexString()
+
+
class IA5StringDecoder(OctetStringDecoder):
protoComponent = char.IA5String()
+
+
class GraphicStringDecoder(OctetStringDecoder):
protoComponent = char.GraphicString()
+
+
class VisibleStringDecoder(OctetStringDecoder):
protoComponent = char.VisibleString()
+
+
class GeneralStringDecoder(OctetStringDecoder):
protoComponent = char.GeneralString()
+
+
class UniversalStringDecoder(OctetStringDecoder):
protoComponent = char.UniversalString()
+
+
class BMPStringDecoder(OctetStringDecoder):
protoComponent = char.BMPString()
+
# "useful" types
class ObjectDescriptorDecoder(OctetStringDecoder):
protoComponent = useful.ObjectDescriptor()
+
+
class GeneralizedTimeDecoder(OctetStringDecoder):
protoComponent = useful.GeneralizedTime()
+
+
class UTCTimeDecoder(OctetStringDecoder):
protoComponent = useful.UTCTime()
+
tagMap = {
univ.Integer.tagSet: IntegerDecoder(),
univ.Boolean.tagSet: BooleanDecoder(),
@@ -559,9 +992,9 @@
univ.ObjectIdentifier.tagSet: ObjectIdentifierDecoder(),
univ.Enumerated.tagSet: IntegerDecoder(),
univ.Real.tagSet: RealDecoder(),
- univ.Sequence.tagSet: SequenceDecoder(), # conflicts with SequenceOf
- univ.Set.tagSet: SetDecoder(), # conflicts with SetOf
- univ.Choice.tagSet: ChoiceDecoder(), # conflicts with Any
+ univ.Sequence.tagSet: SequenceOrSequenceOfDecoder(), # conflicts with SequenceOf
+ univ.Set.tagSet: SetOrSetOfDecoder(), # conflicts with SetOf
+ univ.Choice.tagSet: ChoiceDecoder(), # conflicts with Any
# character string types
char.UTF8String.tagSet: UTF8StringDecoder(),
char.NumericString.tagSet: NumericStringDecoder(),
@@ -590,126 +1023,167 @@
univ.Any.typeId: AnyDecoder()
}
-( stDecodeTag, stDecodeLength, stGetValueDecoder, stGetValueDecoderByAsn1Spec,
- stGetValueDecoderByTag, stTryAsExplicitTag, stDecodeValue,
- stDumpRawValue, stErrorCondition, stStop ) = [x for x in range(10)]
+# Put in non-ambiguous types for faster codec lookup
+for typeDecoder in tagMap.values():
+ if typeDecoder.protoComponent is not None:
+ typeId = typeDecoder.protoComponent.__class__.typeId
+ if typeId is not None and typeId not in typeMap:
+ typeMap[typeId] = typeDecoder
+
+
+(stDecodeTag,
+ stDecodeLength,
+ stGetValueDecoder,
+ stGetValueDecoderByAsn1Spec,
+ stGetValueDecoderByTag,
+ stTryAsExplicitTag,
+ stDecodeValue,
+ stDumpRawValue,
+ stErrorCondition,
+ stStop) = [x for x in range(10)]
+
-class Decoder:
+class Decoder(object):
defaultErrorState = stErrorCondition
-# defaultErrorState = stDumpRawValue
+ # defaultErrorState = stDumpRawValue
defaultRawDecoder = AnyDecoder()
supportIndefLength = True
+
+ # noinspection PyDefaultArgument
def __init__(self, tagMap, typeMap={}):
self.__tagMap = tagMap
self.__typeMap = typeMap
# Tag & TagSet objects caches
self.__tagCache = {}
self.__tagSetCache = {}
-
- def __call__(self, substrate, asn1Spec=None, tagSet=None,
- length=None, state=stDecodeTag, recursiveFlag=1,
- substrateFun=None, allowEoo=False):
+ self.__eooSentinel = ints2octs((0, 0))
+
+ def __call__(self, substrate, asn1Spec=None,
+ tagSet=None, length=None, state=stDecodeTag,
+ decodeFun=None, substrateFun=None,
+ **options):
+
if debug.logger & debug.flagDecoder:
- debug.logger('decoder called at scope %s with state %d, working with up to %d octets of substrate: %s' % (debug.scope, state, len(substrate), debug.hexdump(substrate)))
+ logger = debug.logger
+ else:
+ logger = None
+
+ if logger:
+ logger('decoder called at scope %s with state %d, working with up to %d octets of substrate: %s' % (debug.scope, state, len(substrate), debug.hexdump(substrate)))
+
+ allowEoo = options.pop('allowEoo', False)
+
+ # Look for end-of-octets sentinel
+ if allowEoo and self.supportIndefLength:
+ if substrate[:2] == self.__eooSentinel:
+ if logger:
+ logger('end-of-octets sentinel found')
+ return eoo.endOfOctets, substrate[2:]
+
+ value = noValue
+
+ tagMap = self.__tagMap
+ typeMap = self.__typeMap
+ tagCache = self.__tagCache
+ tagSetCache = self.__tagSetCache
+
fullSubstrate = substrate
- while state != stStop:
- if state == stDecodeTag:
+
+ while state is not stStop:
+ if state is stDecodeTag:
if not substrate:
raise error.SubstrateUnderrunError(
'Short octet stream on tag decoding'
- )
- if not isOctetsType(substrate) and \
- not isinstance(substrate, univ.OctetString):
- raise error.PyAsn1Error('Bad octet stream type')
+ )
# Decode tag
+ isShortTag = True
firstOctet = substrate[0]
substrate = substrate[1:]
- if firstOctet in self.__tagCache:
- lastTag = self.__tagCache[firstOctet]
- else:
- t = oct2int(firstOctet)
- # Look for end-of-octets sentinel
- if t == 0:
- if substrate and oct2int(substrate[0]) == 0:
- if allowEoo and self.supportIndefLength:
- debug.logger and debug.logger & debug.flagDecoder and debug.logger('end-of-octets sentinel found')
- value, substrate = eoo.endOfOctets, substrate[1:]
- state = stStop
- continue
- else:
- raise error.PyAsn1Error('Unexpected end-of-contents sentinel')
- else:
- raise error.PyAsn1Error('Zero tag encountered')
- tagClass = t&0xC0
- tagFormat = t&0x20
- tagId = t&0x1F
+ try:
+ lastTag = tagCache[firstOctet]
+ except KeyError:
+ integerTag = oct2int(firstOctet)
+ tagClass = integerTag & 0xC0
+ tagFormat = integerTag & 0x20
+ tagId = integerTag & 0x1F
if tagId == 0x1F:
+ isShortTag = False
+ lengthOctetIdx = 0
tagId = 0
- while 1:
- if not substrate:
- raise error.SubstrateUnderrunError(
- 'Short octet stream on long tag decoding'
- )
- t = oct2int(substrate[0])
- tagId = tagId << 7 | (t&0x7F)
- substrate = substrate[1:]
- if not t&0x80:
- break
+ try:
+ while True:
+ integerTag = oct2int(substrate[lengthOctetIdx])
+ lengthOctetIdx += 1
+ tagId <<= 7
+ tagId |= (integerTag & 0x7F)
+ if not integerTag & 0x80:
+ break
+ substrate = substrate[lengthOctetIdx:]
+ except IndexError:
+ raise error.SubstrateUnderrunError(
+ 'Short octet stream on long tag decoding'
+ )
lastTag = tag.Tag(
tagClass=tagClass, tagFormat=tagFormat, tagId=tagId
)
- if tagId < 31:
+ if isShortTag:
# cache short tags
- self.__tagCache[firstOctet] = lastTag
+ tagCache[firstOctet] = lastTag
if tagSet is None:
- if firstOctet in self.__tagSetCache:
- tagSet = self.__tagSetCache[firstOctet]
+ if isShortTag:
+ try:
+ tagSet = tagSetCache[firstOctet]
+ except KeyError:
+ # base tag not recovered
+ tagSet = tag.TagSet((), lastTag)
+ tagSetCache[firstOctet] = tagSet
else:
- # base tag not recovered
tagSet = tag.TagSet((), lastTag)
- if firstOctet in self.__tagCache:
- self.__tagSetCache[firstOctet] = tagSet
else:
tagSet = lastTag + tagSet
state = stDecodeLength
- debug.logger and debug.logger & debug.flagDecoder and debug.logger('tag decoded into %s, decoding length' % tagSet)
- if state == stDecodeLength:
+ if logger:
+ logger('tag decoded into %s, decoding length' % tagSet)
+ if state is stDecodeLength:
# Decode length
if not substrate:
raise error.SubstrateUnderrunError(
'Short octet stream on length decoding'
)
- firstOctet = oct2int(substrate[0])
- if firstOctet == 128:
+ firstOctet = oct2int(substrate[0])
+ if firstOctet < 128:
size = 1
- length = -1
- elif firstOctet < 128:
- length, size = firstOctet, 1
- else:
+ length = firstOctet
+ elif firstOctet > 128:
size = firstOctet & 0x7F
# encoded in size bytes
- length = 0
- lengthString = substrate[1:size+1]
+ encodedLength = octs2ints(substrate[1:size + 1])
# missing check on maximum size, which shouldn't be a
# problem, we can handle more than is possible
- if len(lengthString) != size:
+ if len(encodedLength) != size:
raise error.SubstrateUnderrunError(
- '%s<%s at %s' %
- (size, len(lengthString), tagSet)
- )
- for char in lengthString:
- length = (length << 8) | oct2int(char)
- size = size + 1
- substrate = substrate[size:]
- if length != -1 and len(substrate) < length:
- raise error.SubstrateUnderrunError(
- '%d-octet short' % (length - len(substrate))
+ '%s<%s at %s' % (size, len(encodedLength), tagSet)
)
- if length == -1 and not self.supportIndefLength:
- error.PyAsn1Error('Indefinite length encoding not supported by this codec')
+ length = 0
+ for lengthOctet in encodedLength:
+ length <<= 8
+ length |= lengthOctet
+ size += 1
+ else:
+ size = 1
+ length = -1
+
+ substrate = substrate[size:]
+ if length == -1:
+ if not self.supportIndefLength:
+ raise error.PyAsn1Error('Indefinite length encoding not supported by this codec')
+ else:
+ if len(substrate) < length:
+ raise error.SubstrateUnderrunError('%d-octet short' % (length - len(substrate)))
state = stGetValueDecoder
- debug.logger and debug.logger & debug.flagDecoder and debug.logger('value length decoded into %d, payload substrate is: %s' % (length, debug.hexdump(length == -1 and substrate or substrate[:length])))
- if state == stGetValueDecoder:
+ if logger:
+ logger('value length decoded into %d, payload substrate is: %s' % (length, debug.hexdump(length == -1 and substrate or substrate[:length])))
+ if state is stGetValueDecoder:
if asn1Spec is None:
state = stGetValueDecoderByTag
else:
@@ -729,112 +1203,176 @@
# in an incremental, tag-by-tag fashion (this is the case of
# EXPLICIT tag which is most basic). Outermost tag comes first
# from the wire.
- #
- if state == stGetValueDecoderByTag:
- if tagSet in self.__tagMap:
- concreteDecoder = self.__tagMap[tagSet]
- else:
+ #
+ if state is stGetValueDecoderByTag:
+ try:
+ concreteDecoder = tagMap[tagSet]
+ except KeyError:
concreteDecoder = None
if concreteDecoder:
state = stDecodeValue
else:
- _k = tagSet[:1]
- if _k in self.__tagMap:
- concreteDecoder = self.__tagMap[_k]
- else:
+ try:
+ concreteDecoder = tagMap[tagSet[:1]]
+ except KeyError:
concreteDecoder = None
if concreteDecoder:
state = stDecodeValue
else:
state = stTryAsExplicitTag
- if debug.logger and debug.logger & debug.flagDecoder:
- debug.logger('codec %s chosen by a built-in type, decoding %s' % (concreteDecoder and concreteDecoder.__class__.__name__ or "<none>", state == stDecodeValue and 'value' or 'as explicit tag'))
+ if logger:
+ logger('codec %s chosen by a built-in type, decoding %s' % (concreteDecoder and concreteDecoder.__class__.__name__ or "<none>", state is stDecodeValue and 'value' or 'as explicit tag'))
debug.scope.push(concreteDecoder is None and '?' or concreteDecoder.protoComponent.__class__.__name__)
- if state == stGetValueDecoderByAsn1Spec:
- if isinstance(asn1Spec, (dict, tagmap.TagMap)):
- if tagSet in asn1Spec:
- __chosenSpec = asn1Spec[tagSet]
- else:
- __chosenSpec = None
- if debug.logger and debug.logger & debug.flagDecoder:
- debug.logger('candidate ASN.1 spec is a map of:')
- for t, v in asn1Spec.getPosMap().items():
- debug.logger(' %s -> %s' % (t, v.__class__.__name__))
- if asn1Spec.getNegMap():
- debug.logger('but neither of: ')
- for t, v in asn1Spec.getNegMap().items():
- debug.logger(' %s -> %s' % (t, v.__class__.__name__))
- debug.logger('new candidate ASN.1 spec is %s, chosen by %s' % (__chosenSpec is None and '<none>' or __chosenSpec.prettyPrintType(), tagSet))
- else:
- __chosenSpec = asn1Spec
- debug.logger and debug.logger & debug.flagDecoder and debug.logger('candidate ASN.1 spec is %s' % asn1Spec.__class__.__name__)
- if __chosenSpec is not None and (
- tagSet == __chosenSpec.getTagSet() or \
- tagSet in __chosenSpec.getTagMap()
- ):
- # use base type for codec lookup to recover untagged types
- baseTagSet = __chosenSpec.baseTagSet
- if __chosenSpec.typeId is not None and \
- __chosenSpec.typeId in self.__typeMap:
- # ambiguous type
- concreteDecoder = self.__typeMap[__chosenSpec.typeId]
- debug.logger and debug.logger & debug.flagDecoder and debug.logger('value decoder chosen for an ambiguous type by type ID %s' % (__chosenSpec.typeId,))
- elif baseTagSet in self.__tagMap:
- # base type or tagged subtype
- concreteDecoder = self.__tagMap[baseTagSet]
- debug.logger and debug.logger & debug.flagDecoder and debug.logger('value decoder chosen by base %s' % (baseTagSet,))
- else:
- concreteDecoder = None
+ if state is stGetValueDecoderByAsn1Spec:
+ if asn1Spec.__class__ is tagmap.TagMap:
+ try:
+ chosenSpec = asn1Spec[tagSet]
+ except KeyError:
+ chosenSpec = None
+ if logger:
+ logger('candidate ASN.1 spec is a map of:')
+ for firstOctet, v in asn1Spec.presentTypes.items():
+ logger(' %s -> %s' % (firstOctet, v.__class__.__name__))
+ if asn1Spec.skipTypes:
+ logger('but neither of: ')
+ for firstOctet, v in asn1Spec.skipTypes.items():
+ logger(' %s -> %s' % (firstOctet, v.__class__.__name__))
+ logger('new candidate ASN.1 spec is %s, chosen by %s' % (chosenSpec is None and '<none>' or chosenSpec.prettyPrintType(), tagSet))
+ elif tagSet == asn1Spec.tagSet or tagSet in asn1Spec.tagMap:
+ chosenSpec = asn1Spec
+ if logger:
+ logger('candidate ASN.1 spec is %s' % asn1Spec.__class__.__name__)
+ else:
+ chosenSpec = None
+
+ if chosenSpec is not None:
+ try:
+ # ambiguous type or just faster codec lookup
+ concreteDecoder = typeMap[chosenSpec.typeId]
+ if logger:
+ logger('value decoder chosen for an ambiguous type by type ID %s' % (chosenSpec.typeId,))
+ except KeyError:
+ # use base type for codec lookup to recover untagged types
+ baseTagSet = tag.TagSet(chosenSpec.tagSet.baseTag, chosenSpec.tagSet.baseTag)
+ try:
+ # base type or tagged subtype
+ concreteDecoder = tagMap[baseTagSet]
+ if logger:
+ logger('value decoder chosen by base %s' % (baseTagSet,))
+ except KeyError:
+ concreteDecoder = None
if concreteDecoder:
- asn1Spec = __chosenSpec
+ asn1Spec = chosenSpec
state = stDecodeValue
else:
state = stTryAsExplicitTag
else:
concreteDecoder = None
state = stTryAsExplicitTag
- if debug.logger and debug.logger & debug.flagDecoder:
- debug.logger('codec %s chosen by ASN.1 spec, decoding %s' % (state == stDecodeValue and concreteDecoder.__class__.__name__ or "<none>", state == stDecodeValue and 'value' or 'as explicit tag'))
- debug.scope.push(__chosenSpec is None and '?' or __chosenSpec.__class__.__name__)
- if state == stTryAsExplicitTag:
- if tagSet and \
- tagSet[0][1] == tag.tagFormatConstructed and \
- tagSet[0][0] != tag.tagClassUniversal:
+ if logger:
+ logger('codec %s chosen by ASN.1 spec, decoding %s' % (state is stDecodeValue and concreteDecoder.__class__.__name__ or "<none>", state is stDecodeValue and 'value' or 'as explicit tag'))
+ debug.scope.push(chosenSpec is None and '?' or chosenSpec.__class__.__name__)
+ if state is stDecodeValue:
+ if not options.get('recursiveFlag', True) and not substrateFun: # deprecate this
+ substrateFun = lambda a, b, c: (a, b[:c])
+
+ options.update(fullSubstrate=fullSubstrate)
+
+ if length == -1: # indef length
+ value, substrate = concreteDecoder.indefLenValueDecoder(
+ substrate, asn1Spec,
+ tagSet, length, stGetValueDecoder,
+ self, substrateFun,
+ **options
+ )
+ else:
+ value, substrate = concreteDecoder.valueDecoder(
+ substrate, asn1Spec,
+ tagSet, length, stGetValueDecoder,
+ self, substrateFun,
+ **options
+ )
+
+ if logger:
+ logger('codec %s yields type %s, value:\n%s\n...remaining substrate is: %s' % (concreteDecoder.__class__.__name__, value.__class__.__name__, isinstance(value, base.Asn1Item) and value.prettyPrint() or value, substrate and debug.hexdump(substrate) or '<none>'))
+
+ state = stStop
+ break
+ if state is stTryAsExplicitTag:
+ if tagSet and tagSet[0].tagFormat == tag.tagFormatConstructed and tagSet[0].tagClass != tag.tagClassUniversal:
# Assume explicit tagging
concreteDecoder = explicitTagDecoder
state = stDecodeValue
- else:
+ else:
concreteDecoder = None
state = self.defaultErrorState
- debug.logger and debug.logger & debug.flagDecoder and debug.logger('codec %s chosen, decoding %s' % (concreteDecoder and concreteDecoder.__class__.__name__ or "<none>", state == stDecodeValue and 'value' or 'as failure'))
- if state == stDumpRawValue:
+ if logger:
+ logger('codec %s chosen, decoding %s' % (concreteDecoder and concreteDecoder.__class__.__name__ or "<none>", state is stDecodeValue and 'value' or 'as failure'))
+ if state is stDumpRawValue:
concreteDecoder = self.defaultRawDecoder
- debug.logger and debug.logger & debug.flagDecoder and debug.logger('codec %s chosen, decoding value' % concreteDecoder.__class__.__name__)
+ if logger:
+ logger('codec %s chosen, decoding value' % concreteDecoder.__class__.__name__)
state = stDecodeValue
- if state == stDecodeValue:
- if recursiveFlag == 0 and not substrateFun: # legacy
- substrateFun = lambda a,b,c: (a,b[:c])
- if length == -1: # indef length
- value, substrate = concreteDecoder.indefLenValueDecoder(
- fullSubstrate, substrate, asn1Spec, tagSet, length,
- stGetValueDecoder, self, substrateFun
- )
- else:
- value, substrate = concreteDecoder.valueDecoder(
- fullSubstrate, substrate, asn1Spec, tagSet, length,
- stGetValueDecoder, self, substrateFun
- )
- state = stStop
- debug.logger and debug.logger & debug.flagDecoder and debug.logger('codec %s yields type %s, value:\n%s\n...remaining substrate is: %s' % (concreteDecoder.__class__.__name__, value.__class__.__name__, value.prettyPrint(), substrate and debug.hexdump(substrate) or '<none>'))
- if state == stErrorCondition:
+ if state is stErrorCondition:
raise error.PyAsn1Error(
- '%s not in asn1Spec: %s' % (tagSet, asn1Spec)
+ '%s not in asn1Spec: %r' % (tagSet, asn1Spec)
)
- if debug.logger and debug.logger & debug.flagDecoder:
+ if logger:
debug.scope.pop()
- debug.logger('decoder left scope %s, call completed' % debug.scope)
+ logger('decoder left scope %s, call completed' % debug.scope)
return value, substrate
-
+
+
+#: Turns BER octet stream into an ASN.1 object.
+#:
+#: Takes BER octet-stream and decode it into an ASN.1 object
+#: (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative) which
+#: may be a scalar or an arbitrary nested structure.
+#:
+#: Parameters
+#: ----------
+#: substrate: :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2)
+#: BER octet-stream
+#:
+#: Keyword Args
+#: ------------
+#: asn1Spec: any pyasn1 type object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+#: A pyasn1 type object to act as a template guiding the decoder. Depending on the ASN.1 structure
+#: being decoded, *asn1Spec* may or may not be required. Most common reason for
+#: it to require is that ASN.1 structure is encoded in *IMPLICIT* tagging mode.
+#:
+#: Returns
+#: -------
+#: : :py:class:`tuple`
+#: A tuple of pyasn1 object recovered from BER substrate (:py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#: and the unprocessed trailing portion of the *substrate* (may be empty)
+#:
+#: Raises
+#: ------
+#: :py:class:`~pyasn1.error.PyAsn1Error`
+#: On decoding errors
+#:
+#: Examples
+#: --------
+#: Decode BER serialisation without ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#: >>> s, _ = decode(b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03')
+#: >>> str(s)
+#: SequenceOf:
+#: 1 2 3
+#:
+#: Decode BER serialisation with ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#: >>> seq = SequenceOf(componentType=Integer())
+#: >>> s, _ = decode(b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03', asn1Spec=seq)
+#: >>> str(s)
+#: SequenceOf:
+#: 1 2 3
+#:
decode = Decoder(tagMap, typeMap)
# XXX
diff -u -r 1.9.67/google_appengine/lib/pyasn1/pyasn1/codec/ber/encoder.py 1.9.68/google_appengine/lib/pyasn1/pyasn1/codec/ber/encoder.py
--- 1.9.67/google_appengine/lib/pyasn1/pyasn1/codec/ber/encoder.py 2018-03-09 20:05:30.000000000 -0800
+++ 1.9.68/google_appengine/lib/pyasn1/pyasn1/codec/ber/encoder.py 2018-04-03 21:35:14.000000000 -0700
@@ -1,228 +1,320 @@
-# BER encoder
-from pyasn1.type import base, tag, univ, char, useful
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2017, Ilya Etingof <[email protected]>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+from pyasn1 import debug
+from pyasn1 import error
from pyasn1.codec.ber import eoo
-from pyasn1.compat.octets import int2oct, oct2int, ints2octs, null, str2octs
-from pyasn1 import debug, error
-
-class Error(Exception): pass
-
-class AbstractItemEncoder:
- supportIndefLenMode = 1
- def encodeTag(self, t, isConstructed):
- tagClass, tagFormat, tagId = t.asTuple() # this is a hotspot
- v = tagClass | tagFormat
+from pyasn1.compat.integer import to_bytes
+from pyasn1.compat.octets import (int2oct, oct2int, ints2octs, null,
+ str2octs, isOctetsType)
+from pyasn1.type import char
+from pyasn1.type import tag
+from pyasn1.type import univ
+from pyasn1.type import useful
+
+__all__ = ['encode']
+
+
+class AbstractItemEncoder(object):
+ supportIndefLenMode = True
+
+ # An outcome of otherwise legit call `encodeFun(eoo.endOfOctets)`
+ eooIntegerSubstrate = (0, 0)
+ eooOctetsSubstrate = ints2octs(eooIntegerSubstrate)
+
+ # noinspection PyMethodMayBeStatic
+ def encodeTag(self, singleTag, isConstructed):
+ tagClass, tagFormat, tagId = singleTag
+ encodedTag = tagClass | tagFormat
if isConstructed:
- v = v|tag.tagFormatConstructed
+ encodedTag |= tag.tagFormatConstructed
if tagId < 31:
- return int2oct(v|tagId)
+ return encodedTag | tagId,
else:
- s = int2oct(tagId&0x7f)
- tagId = tagId >> 7
+ substrate = tagId & 0x7f,
+ tagId >>= 7
while tagId:
- s = int2oct(0x80|(tagId&0x7f)) + s
- tagId = tagId >> 7
- return int2oct(v|0x1F) + s
+ substrate = (0x80 | (tagId & 0x7f),) + substrate
+ tagId >>= 7
+ return (encodedTag | 0x1F,) + substrate
def encodeLength(self, length, defMode):
if not defMode and self.supportIndefLenMode:
- return int2oct(0x80)
+ return (0x80,)
if length < 0x80:
- return int2oct(length)
+ return length,
else:
- substrate = null
+ substrate = ()
while length:
- substrate = int2oct(length&0xff) + substrate
- length = length >> 8
+ substrate = (length & 0xff,) + substrate
+ length >>= 8
substrateLen = len(substrate)
if substrateLen > 126:
- raise Error('Length octets overflow (%d)' % substrateLen)
- return int2oct(0x80 | substrateLen) + substrate
+ raise error.PyAsn1Error('Length octets overflow (%d)' % substrateLen)
+ return (0x80 | substrateLen,) + substrate
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
- raise Error('Not implemented')
+ def encodeValue(self, value, asn1Spec, encodeFun, **options):
+ raise error.PyAsn1Error('Not implemented')
- def _encodeEndOfOctets(self, encodeFun, defMode):
- if defMode or not self.supportIndefLenMode:
- return null
- else:
- return encodeFun(eoo.endOfOctets, defMode)
-
- def encode(self, encodeFun, value, defMode, maxChunkSize):
- substrate, isConstructed = self.encodeValue(
- encodeFun, value, defMode, maxChunkSize
- )
- tagSet = value.getTagSet()
- if tagSet:
- if not isConstructed: # primitive form implies definite mode
- defMode = 1
- return self.encodeTag(
- tagSet[-1], isConstructed
- ) + self.encodeLength(
- len(substrate), defMode
- ) + substrate + self._encodeEndOfOctets(encodeFun, defMode)
+ def encode(self, value, asn1Spec=None, encodeFun=None, **options):
+
+ if asn1Spec is None:
+ tagSet = value.tagSet
else:
- return substrate # untagged value
+ tagSet = asn1Spec.tagSet
-class EndOfOctetsEncoder(AbstractItemEncoder):
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
- return null, 0
+ # untagged item?
+ if not tagSet:
+ substrate, isConstructed, isOctets = self.encodeValue(
+ value, asn1Spec, encodeFun, **options
+ )
+ return substrate
-class ExplicitlyTaggedItemEncoder(AbstractItemEncoder):
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
- if isinstance(value, base.AbstractConstructedAsn1Item):
- value = value.clone(tagSet=value.getTagSet()[:-1],
- cloneValueFlag=1)
- else:
- value = value.clone(tagSet=value.getTagSet()[:-1])
- return encodeFun(value, defMode, maxChunkSize), 1
+ defMode = options.get('defMode', True)
+
+ for idx, singleTag in enumerate(tagSet.superTags):
+
+ defModeOverride = defMode
+
+ # base tag?
+ if not idx:
+ substrate, isConstructed, isOctets = self.encodeValue(
+ value, asn1Spec, encodeFun, **options
+ )
+
+ if not substrate and isConstructed and options.get('ifNotEmpty', False):
+ return substrate
+
+ # primitive form implies definite mode
+ if not isConstructed:
+ defModeOverride = True
+
+ header = self.encodeTag(singleTag, isConstructed)
+ header += self.encodeLength(len(substrate), defModeOverride)
+
+ if isOctets:
+ substrate = ints2octs(header) + substrate
+
+ if not defModeOverride:
+ substrate += self.eooOctetsSubstrate
+
+ else:
+ substrate = header + substrate
+
+ if not defModeOverride:
+ substrate += self.eooIntegerSubstrate
+
+ if not isOctets:
+ substrate = ints2octs(substrate)
+
+ return substrate
+
+
+class EndOfOctetsEncoder(AbstractItemEncoder):
+ def encodeValue(self, value, asn1Spec, encodeFun, **options):
+ return null, False, True
-explicitlyTaggedItemEncoder = ExplicitlyTaggedItemEncoder()
class BooleanEncoder(AbstractItemEncoder):
- supportIndefLenMode = 0
- _true = ints2octs((1,))
- _false = ints2octs((0,))
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
- return value and self._true or self._false, 0
+ supportIndefLenMode = False
+
+ def encodeValue(self, value, asn1Spec, encodeFun, **options):
+ return value and (1,) or (0,), False, False
+
class IntegerEncoder(AbstractItemEncoder):
- supportIndefLenMode = 0
+ supportIndefLenMode = False
supportCompactZero = False
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
- if value == 0: # shortcut for zero value
+
+ def encodeValue(self, value, asn1Spec, encodeFun, **options):
+ if value == 0:
+ # de-facto way to encode zero
if self.supportCompactZero:
- # this seems to be a correct way for encoding zeros
- return null, 0
+ return (), False, False
else:
- # this seems to be a widespread way for encoding zeros
- return ints2octs((0,)), 0
- octets = []
- value = int(value) # to save on ops on asn1 type
- while 1:
- octets.insert(0, value & 0xff)
- if value == 0 or value == -1:
- break
- value = value >> 8
- if value == 0 and octets[0] & 0x80:
- octets.insert(0, 0)
- while len(octets) > 1 and \
- (octets[0] == 0 and octets[1] & 0x80 == 0 or \
- octets[0] == 0xff and octets[1] & 0x80 != 0):
- del octets[0]
- return ints2octs(octets), 0
+ return (0,), False, False
+
+ return to_bytes(int(value), signed=True), False, True
+
class BitStringEncoder(AbstractItemEncoder):
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
- if not maxChunkSize or len(value) <= maxChunkSize*8:
- out_len = (len(value) + 7) // 8
- out_list = out_len * [0]
- j = 7
- i = -1
- for val in value:
- j += 1
- if j == 8:
- i += 1
- j = 0
- out_list[i] = out_list[i] | val << (7-j)
- return int2oct(7-j) + ints2octs(out_list), 0
- else:
- pos = 0; substrate = null
- while 1:
- # count in octets
- v = value.clone(value[pos*8:pos*8+maxChunkSize*8])
- if not v:
- break
- substrate = substrate + encodeFun(v, defMode, maxChunkSize)
- pos = pos + maxChunkSize
- return substrate, 1
+ def encodeValue(self, value, asn1Spec, encodeFun, **options):
+ if asn1Spec is not None:
+ # TODO: try to avoid ASN.1 schema instantiation
+ value = asn1Spec.clone(value)
+
+ valueLength = len(value)
+ if valueLength % 8:
+ alignedValue = value << (8 - valueLength % 8)
+ else:
+ alignedValue = value
+
+ maxChunkSize = options.get('maxChunkSize', 0)
+ if not maxChunkSize or len(alignedValue) <= maxChunkSize * 8:
+ substrate = alignedValue.asOctets()
+ return int2oct(len(substrate) * 8 - valueLength) + substrate, False, True
+
+ baseTag = value.tagSet.baseTag
+
+ # strip off explicit tags
+ if baseTag:
+ tagSet = tag.TagSet(baseTag, baseTag)
+ else:
+ tagSet = tag.TagSet()
+
+ alignedValue = alignedValue.clone(tagSet=tagSet)
+
+ stop = 0
+ substrate = null
+ while stop < valueLength:
+ start = stop
+ stop = min(start + maxChunkSize * 8, valueLength)
+ substrate += encodeFun(alignedValue[start:stop], asn1Spec, **options)
+
+ return substrate, True, True
+
class OctetStringEncoder(AbstractItemEncoder):
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
- if not maxChunkSize or len(value) <= maxChunkSize:
- return value.asOctets(), 0
- else:
- pos = 0; substrate = null
- while 1:
- v = value.clone(value[pos:pos+maxChunkSize])
- if not v:
+
+ def encodeValue(self, value, asn1Spec, encodeFun, **options):
+
+ if asn1Spec is None:
+ substrate = value.asOctets()
+
+ elif not isOctetsType(value):
+ substrate = asn1Spec.clone(value).asOctets()
+
+ else:
+ substrate = value
+
+ maxChunkSize = options.get('maxChunkSize', 0)
+
+ if not maxChunkSize or len(substrate) <= maxChunkSize:
+ return substrate, False, True
+
+ else:
+
+ # strip off explicit tags for inner chunks
+
+ if asn1Spec is None:
+ baseTag = value.tagSet.baseTag
+
+ # strip off explicit tags
+ if baseTag:
+ tagSet = tag.TagSet(baseTag, baseTag)
+ else:
+ tagSet = tag.TagSet()
+
+ asn1Spec = value.clone(tagSet=tagSet)
+
+ elif not isOctetsType(value):
+ baseTag = asn1Spec.tagSet.baseTag
+
+ # strip off explicit tags
+ if baseTag:
+ tagSet = tag.TagSet(baseTag, baseTag)
+ else:
+ tagSet = tag.TagSet()
+
+ asn1Spec = asn1Spec.clone(tagSet=tagSet)
+
+ pos = 0
+ substrate = null
+
+ while True:
+ chunk = value[pos:pos + maxChunkSize]
+ if not chunk:
break
- substrate = substrate + encodeFun(v, defMode, maxChunkSize)
- pos = pos + maxChunkSize
- return substrate, 1
+
+ substrate += encodeFun(chunk, asn1Spec, **options)
+ pos += maxChunkSize
+
+ return substrate, True, True
+
class NullEncoder(AbstractItemEncoder):
- supportIndefLenMode = 0
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
- return null, 0
+ supportIndefLenMode = False
+
+ def encodeValue(self, value, asn1Spec, encodeFun, **options):
+ return null, False, True
+
class ObjectIdentifierEncoder(AbstractItemEncoder):
- supportIndefLenMode = 0
- precomputedValues = {
- (1, 3, 6, 1, 2): (43, 6, 1, 2),
- (1, 3, 6, 1, 4): (43, 6, 1, 4)
- }
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ supportIndefLenMode = False
+
+ def encodeValue(self, value, asn1Spec, encodeFun, **options):
+ if asn1Spec is not None:
+ value = asn1Spec.clone(value)
+
oid = value.asTuple()
- if oid[:5] in self.precomputedValues:
- octets = self.precomputedValues[oid[:5]]
- oid = oid[5:]
- else:
- if len(oid) < 2:
- raise error.PyAsn1Error('Short OID %s' % (value,))
-
- octets = ()
-
- # Build the first twos
- if oid[0] == 0 and 0 <= oid[1] <= 39:
- oid = (oid[1],) + oid[2:]
- elif oid[0] == 1 and 0 <= oid[1] <= 39:
- oid = (oid[1] + 40,) + oid[2:]
- elif oid[0] == 2:
- oid = (oid[1] + 80,) + oid[2:]
+
+ # Build the first pair
+ try:
+ first = oid[0]
+ second = oid[1]
+
+ except IndexError:
+ raise error.PyAsn1Error('Short OID %s' % (value,))
+
+ if 0 <= second <= 39:
+ if first == 1:
+ oid = (second + 40,) + oid[2:]
+ elif first == 0:
+ oid = (second,) + oid[2:]
+ elif first == 2:
+ oid = (second + 80,) + oid[2:]
else:
- raise error.PyAsn1Error(
- 'Impossible initial arcs %s at %s' % (oid[:2], value)
- )
+ raise error.PyAsn1Error('Impossible first/second arcs at %s' % (value,))
+ elif first == 2:
+ oid = (second + 80,) + oid[2:]
+ else:
+ raise error.PyAsn1Error('Impossible first/second arcs at %s' % (value,))
+
+ octets = ()
# Cycle through subIds
- for subId in oid:
- if subId > -1 and subId < 128:
+ for subOid in oid:
+ if 0 <= subOid <= 127:
# Optimize for the common case
- octets = octets + (subId & 0x7f,)
- elif subId < 0:
- raise error.PyAsn1Error(
- 'Negative OID arc %s at %s' % (subId, value)
- )
- else:
+ octets += (subOid,)
+ elif subOid > 127:
# Pack large Sub-Object IDs
- res = (subId & 0x7f,)
- subId = subId >> 7
- while subId > 0:
- res = (0x80 | (subId & 0x7f),) + res
- subId = subId >> 7
+ res = (subOid & 0x7f,)
+ subOid >>= 7
+ while subOid:
+ res = (0x80 | (subOid & 0x7f),) + res
+ subOid >>= 7
# Add packed Sub-Object ID to resulted Object ID
octets += res
+ else:
+ raise error.PyAsn1Error('Negative OID arc %s at %s' % (subOid, value))
+
+ return octets, False, False
- return ints2octs(octets), 0
class RealEncoder(AbstractItemEncoder):
supportIndefLenMode = 0
- binEncBase = 2 # set to None to choose encoding base automatically
- def _dropFloatingPoint(self, m, encbase, e):
+ binEncBase = 2 # set to None to choose encoding base automatically
+
+ @staticmethod
+ def _dropFloatingPoint(m, encbase, e):
ms, es = 1, 1
if m < 0:
ms = -1 # mantissa sign
if e < 0:
es = -1 # exponenta sign
- m *= ms
+ m *= ms
if encbase == 8:
- m = m*2**(abs(e) % 3 * es)
+ m *= 2 ** (abs(e) % 3 * es)
e = abs(e) // 3 * es
elif encbase == 16:
- m = m*2**(abs(e) % 4 * es)
+ m *= 2 ** (abs(e) % 4 * es)
e = abs(e) // 4 * es
- while 1:
+ while True:
if int(m) != m:
m *= encbase
e -= 1
@@ -232,41 +324,46 @@
def _chooseEncBase(self, value):
m, b, e = value
- base = [2, 8, 16]
- if value.binEncBase in base:
+ encBase = [2, 8, 16]
+ if value.binEncBase in encBase:
return self._dropFloatingPoint(m, value.binEncBase, e)
- elif self.binEncBase in base:
+ elif self.binEncBase in encBase:
return self._dropFloatingPoint(m, self.binEncBase, e)
# auto choosing base 2/8/16
mantissa = [m, m, m]
exponenta = [e, e, e]
- encbase = 2
+ sign = 1
+ encbase = 2
e = float('inf')
for i in range(3):
- sign, mantissa[i], base[i], exponenta[i] = \
- self._dropFloatingPoint(mantissa[i], base[i], exponenta[i])
- if abs(exponenta[i]) < abs(e) or \
- (abs(exponenta[i]) == abs(e) and mantissa[i] < m):
+ (sign,
+ mantissa[i],
+ encBase[i],
+ exponenta[i]) = self._dropFloatingPoint(mantissa[i], encBase[i], exponenta[i])
+ if abs(exponenta[i]) < abs(e) or (abs(exponenta[i]) == abs(e) and mantissa[i] < m):
e = exponenta[i]
m = int(mantissa[i])
- encbase = base[i]
+ encbase = encBase[i]
return sign, m, encbase, e
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
- if value.isPlusInfinity():
- return int2oct(0x40), 0
- if value.isMinusInfinity():
- return int2oct(0x41), 0
+ def encodeValue(self, value, asn1Spec, encodeFun, **options):
+ if asn1Spec is not None:
+ value = asn1Spec.clone(value)
+
+ if value.isPlusInf:
+ return (0x40,), False, False
+ if value.isMinusInf:
+ return (0x41,), False, False
m, b, e = value
if not m:
- return null, 0
+ return null, False, True
if b == 10:
- return str2octs('\x03%dE%s%d' % (m, e == 0 and '+' or '', e)), 0
+ return str2octs('\x03%dE%s%d' % (m, e == 0 and '+' or '', e)), False, True
elif b == 2:
- fo = 0x80 # binary encoding
+ fo = 0x80 # binary encoding
ms, m, encbase, e = self._chooseEncBase(value)
- if ms < 0: # mantissa sign
- fo = fo | 0x40 # sign bit
+ if ms < 0: # mantissa sign
+ fo |= 0x40 # sign bit
# exponenta & mantissa normalization
if encbase == 2:
while m & 0x1 == 0:
@@ -277,24 +374,24 @@
m >>= 3
e += 1
fo |= 0x10
- else: # encbase = 16
+ else: # encbase = 16
while m & 0xf == 0:
m >>= 4
e += 1
fo |= 0x20
- sf = 0 # scale factor
+ sf = 0 # scale factor
while m & 0x1 == 0:
m >>= 1
sf += 1
if sf > 3:
- raise error.PyAsn1Error('Scale factor overflow') # bug if raised
+ raise error.PyAsn1Error('Scale factor overflow') # bug if raised
fo |= sf << 2
eo = null
if e == 0 or e == -1:
- eo = int2oct(e&0xff)
- else:
+ eo = int2oct(e & 0xff)
+ else:
while e not in (0, -1):
- eo = int2oct(e&0xff) + eo
+ eo = int2oct(e & 0xff) + eo
e >>= 8
if e == 0 and eo and oct2int(eo[0]) & 0x80:
eo = int2oct(0) + eo
@@ -311,51 +408,129 @@
fo |= 2
else:
fo |= 3
- eo = int2oct(n&0xff) + eo
+ eo = int2oct(n & 0xff) + eo
po = null
while m:
- po = int2oct(m&0xff) + po
+ po = int2oct(m & 0xff) + po
m >>= 8
substrate = int2oct(fo) + eo + po
- return substrate, 0
+ return substrate, False, True
else:
raise error.PyAsn1Error('Prohibited Real base %s' % b)
+
class SequenceEncoder(AbstractItemEncoder):
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
- value.setDefaultComponents()
- value.verifySizeSpec()
- substrate = null; idx = len(value)
- while idx > 0:
- idx = idx - 1
- if value[idx] is None: # Optional component
- continue
- component = value.getDefaultComponentByPosition(idx)
- if component is not None and component == value[idx]:
- continue
- substrate = encodeFun(
- value[idx], defMode, maxChunkSize
- ) + substrate
- return substrate, 1
+ omitEmptyOptionals = False
+
+ # TODO: handling three flavors of input is too much -- split over codecs
+
+ def encodeValue(self, value, asn1Spec, encodeFun, **options):
+
+ substrate = null
+
+ if asn1Spec is None:
+ # instance of ASN.1 schema
+ value.verifySizeSpec()
+
+ namedTypes = value.componentType
+
+ for idx, component in enumerate(value.values()):
+ if namedTypes:
+ namedType = namedTypes[idx]
+
+ if namedType.isOptional and not component.isValue:
+ continue
+
+ if namedType.isDefaulted and component == namedType.asn1Object:
+ continue
+
+ if self.omitEmptyOptionals:
+ options.update(ifNotEmpty=namedType.isOptional)
+
+ chunk = encodeFun(component, asn1Spec, **options)
+
+ # wrap open type blob if needed
+ if namedTypes and namedType.openType:
+ wrapType = namedType.asn1Object
+ if wrapType.tagSet and not wrapType.isSameTypeWith(component):
+ chunk = encodeFun(chunk, wrapType, **options)
+
+ substrate += chunk
+
+ else:
+ # bare Python value + ASN.1 schema
+ for idx, namedType in enumerate(asn1Spec.componentType.namedTypes):
+
+ try:
+ component = value[namedType.name]
+
+ except KeyError:
+ raise error.PyAsn1Error('Component name "%s" not found in %r' % (namedType.name, value))
+
+ if namedType.isOptional and namedType.name not in value:
+ continue
+
+ if namedType.isDefaulted and component == namedType.asn1Object:
+ continue
+
+ if self.omitEmptyOptionals:
+ options.update(ifNotEmpty=namedType.isOptional)
+
+ chunk = encodeFun(component, asn1Spec[idx], **options)
+
+ # wrap open type blob if needed
+ if namedType.openType:
+ wrapType = namedType.asn1Object
+ if wrapType.tagSet and not wrapType.isSameTypeWith(component):
+ chunk = encodeFun(chunk, wrapType, **options)
+
+ substrate += chunk
+
+ return substrate, True, True
+
class SequenceOfEncoder(AbstractItemEncoder):
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
- value.verifySizeSpec()
- substrate = null; idx = len(value)
- while idx > 0:
- idx = idx - 1
- substrate = encodeFun(
- value[idx], defMode, maxChunkSize
- ) + substrate
- return substrate, 1
+ def encodeValue(self, value, asn1Spec, encodeFun, **options):
+ if asn1Spec is None:
+ value.verifySizeSpec()
+ else:
+ asn1Spec = asn1Spec.componentType
+
+ substrate = null
+
+ for idx, component in enumerate(value):
+ substrate += encodeFun(value[idx], asn1Spec, **options)
+
+ return substrate, True, True
+
class ChoiceEncoder(AbstractItemEncoder):
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
- return encodeFun(value.getComponent(), defMode, maxChunkSize), 1
+ def encodeValue(self, value, asn1Spec, encodeFun, **options):
+ if asn1Spec is None:
+ component = value.getComponent()
+ else:
+ names = [namedType.name for namedType in asn1Spec.componentType.namedTypes
+ if namedType.name in value]
+ if len(names) != 1:
+ raise error.PyAsn1Error('%s components for Choice at %r' % (len(names) and 'Multiple ' or 'None ', value))
+
+ name = names[0]
+
+ component = value[name]
+ asn1Spec = asn1Spec[name]
+
+ return encodeFun(component, asn1Spec, **options), True, True
+
class AnyEncoder(OctetStringEncoder):
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
- return value.asOctets(), defMode == 0
+ def encodeValue(self, value, asn1Spec, encodeFun, **options):
+ if asn1Spec is None:
+ value = value.asOctets()
+ elif not isOctetsType(value):
+ value = asn1Spec.clone(value).asOctets()
+
+ return value, not options.get('defMode', True), True
+
tagMap = {
eoo.endOfOctets.tagSet: EndOfOctetsEncoder(),
@@ -386,48 +561,161 @@
# useful types
useful.ObjectDescriptor.tagSet: OctetStringEncoder(),
useful.GeneralizedTime.tagSet: OctetStringEncoder(),
- useful.UTCTime.tagSet: OctetStringEncoder()
- }
+ useful.UTCTime.tagSet: OctetStringEncoder()
+}
-# Type-to-codec map for ambiguous ASN.1 types
+# Put in ambiguous & non-ambiguous types for faster codec lookup
typeMap = {
+ univ.Boolean.typeId: BooleanEncoder(),
+ univ.Integer.typeId: IntegerEncoder(),
+ univ.BitString.typeId: BitStringEncoder(),
+ univ.OctetString.typeId: OctetStringEncoder(),
+ univ.Null.typeId: NullEncoder(),
+ univ.ObjectIdentifier.typeId: ObjectIdentifierEncoder(),
+ univ.Enumerated.typeId: IntegerEncoder(),
+ univ.Real.typeId: RealEncoder(),
+ # Sequence & Set have same tags as SequenceOf & SetOf
univ.Set.typeId: SequenceEncoder(),
univ.SetOf.typeId: SequenceOfEncoder(),
univ.Sequence.typeId: SequenceEncoder(),
univ.SequenceOf.typeId: SequenceOfEncoder(),
univ.Choice.typeId: ChoiceEncoder(),
- univ.Any.typeId: AnyEncoder()
- }
+ univ.Any.typeId: AnyEncoder(),
+ # character string types
+ char.UTF8String.typeId: OctetStringEncoder(),
+ char.NumericString.typeId: OctetStringEncoder(),
+ char.PrintableString.typeId: OctetStringEncoder(),
+ char.TeletexString.typeId: OctetStringEncoder(),
+ char.VideotexString.typeId: OctetStringEncoder(),
+ char.IA5String.typeId: OctetStringEncoder(),
+ char.GraphicString.typeId: OctetStringEncoder(),
+ char.VisibleString.typeId: OctetStringEncoder(),
+ char.GeneralString.typeId: OctetStringEncoder(),
+ char.UniversalString.typeId: OctetStringEncoder(),
+ char.BMPString.typeId: OctetStringEncoder(),
+ # useful types
+ useful.ObjectDescriptor.typeId: OctetStringEncoder(),
+ useful.GeneralizedTime.typeId: OctetStringEncoder(),
+ useful.UTCTime.typeId: OctetStringEncoder()
+}
-class Encoder:
- supportIndefLength = True
+
+class Encoder(object):
+ fixedDefLengthMode = None
+ fixedChunkSize = None
+
+ # noinspection PyDefaultArgument
def __init__(self, tagMap, typeMap={}):
self.__tagMap = tagMap
self.__typeMap = typeMap
- def __call__(self, value, defMode=True, maxChunkSize=0):
- if not defMode and not self.supportIndefLength:
- raise error.PyAsn1Error('Indefinite length encoding not supported by this codec')
- debug.logger & debug.flagEncoder and debug.logger('encoder called in %sdef mode, chunk size %s for type %s, value:\n%s' % (not defMode and 'in' or '', maxChunkSize, value.prettyPrintType(), value.prettyPrint()))
- tagSet = value.getTagSet()
- if len(tagSet) > 1:
- concreteEncoder = explicitlyTaggedItemEncoder
- else:
- if value.typeId is not None and value.typeId in self.__typeMap:
- concreteEncoder = self.__typeMap[value.typeId]
- elif tagSet in self.__tagMap:
- concreteEncoder = self.__tagMap[tagSet]
+ def __call__(self, value, asn1Spec=None, **options):
+ try:
+ if asn1Spec is None:
+ typeId = value.typeId
else:
- tagSet = value.baseTagSet
- if tagSet in self.__tagMap:
- concreteEncoder = self.__tagMap[tagSet]
- else:
- raise Error('No encoder for %s' % (value,))
- debug.logger & debug.flagEncoder and debug.logger('using value codec %s chosen by %s' % (concreteEncoder.__class__.__name__, tagSet))
- substrate = concreteEncoder.encode(
- self, value, defMode, maxChunkSize
- )
- debug.logger & debug.flagEncoder and debug.logger('built %s octets of substrate: %s\nencoder completed' % (len(substrate), debug.hexdump(substrate)))
+ typeId = asn1Spec.typeId
+
+ except AttributeError:
+ raise error.PyAsn1Error('Value %r is not ASN.1 type instance '
+ 'and "asn1Spec" not given' % (value,))
+
+ if debug.logger & debug.flagEncoder:
+ logger = debug.logger
+ else:
+ logger = None
+
+ if logger:
+ logger('encoder called in %sdef mode, chunk size %s for '
+ 'type %s, value:\n%s' % (not options.get('defMode', True) and 'in' or '', options.get('maxChunkSize', 0), asn1Spec is None and value.prettyPrintType() or asn1Spec.prettyPrintType(), value))
+
+ if self.fixedDefLengthMode is not None:
+ options.update(defMode=self.fixedDefLengthMode)
+
+ if self.fixedChunkSize is not None:
+ options.update(maxChunkSize=self.fixedChunkSize)
+
+
+ try:
+ concreteEncoder = self.__typeMap[typeId]
+
+ if logger:
+ logger('using value codec %s chosen by type ID %s' % (concreteEncoder.__class__.__name__, typeId))
+
+ except KeyError:
+ if asn1Spec is None:
+ tagSet = value.tagSet
+ else:
+ tagSet = asn1Spec.tagSet
+
+ # use base type for codec lookup to recover untagged types
+ baseTagSet = tag.TagSet(tagSet.baseTag, tagSet.baseTag)
+
+ try:
+ concreteEncoder = self.__tagMap[baseTagSet]
+
+ except KeyError:
+ raise error.PyAsn1Error('No encoder for %r (%s)' % (value, tagSet))
+
+ if logger:
+ logger('using value codec %s chosen by tagSet %s' % (concreteEncoder.__class__.__name__, tagSet))
+
+ substrate = concreteEncoder.encode(value, asn1Spec, self, **options)
+
+ if logger:
+ logger('codec %s built %s octets of substrate: %s\nencoder completed' % (concreteEncoder, len(substrate), debug.hexdump(substrate)))
+
return substrate
+#: Turns ASN.1 object into BER octet stream.
+#:
+#: Takes any ASN.1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#: walks all its components recursively and produces a BER octet stream.
+#:
+#: Parameters
+#: ----------
+#: value: either a Python or pyasn1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#: A Python or pyasn1 object to encode. If Python object is given, `asnSpec`
+#: parameter is required to guide the encoding process.
+#:
+#: Keyword Args
+#: ------------
+#: asn1Spec:
+#: Optional ASN.1 schema or value object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+#:
+#: defMode: :py:class:`bool`
+#: If `False`, produces indefinite length encoding
+#:
+#: maxChunkSize: :py:class:`int`
+#: Maximum chunk size in chunked encoding mode (0 denotes unlimited chunk size)
+#:
+#: Returns
+#: -------
+#: : :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2)
+#: Given ASN.1 object encoded into BER octetstream
+#:
+#: Raises
+#: ------
+#: :py:class:`~pyasn1.error.PyAsn1Error`
+#: On encoding errors
+#:
+#: Examples
+#: --------
+#: Encode Python value into BER with ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#: >>> seq = SequenceOf(componentType=Integer())
+#: >>> encode([1, 2, 3], asn1Spec=seq)
+#: b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03'
+#:
+#: Encode ASN.1 value object into BER
+#:
+#: .. code-block:: pycon
+#:
+#: >>> seq = SequenceOf(componentType=Integer())
+#: >>> seq.extend([1, 2, 3])
+#: >>> encode(seq)
+#: b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03'
+#:
encode = Encoder(tagMap, typeMap)
diff -u -r 1.9.67/google_appengine/lib/pyasn1/pyasn1/codec/ber/eoo.py 1.9.68/google_appengine/lib/pyasn1/pyasn1/codec/ber/eoo.py
--- 1.9.67/google_appengine/lib/pyasn1/pyasn1/codec/ber/eoo.py 2018-03-09 20:05:30.000000000 -0800
+++ 1.9.68/google_appengine/lib/pyasn1/pyasn1/codec/ber/eoo.py 2018-04-03 21:35:14.000000000 -0700
@@ -1,8 +1,28 @@
-from pyasn1.type import base, tag
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2017, Ilya Etingof <[email protected]>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+from pyasn1.type import base
+from pyasn1.type import tag
+
+__all__ = ['endOfOctets']
+
class EndOfOctets(base.AbstractSimpleAsn1Item):
defaultValue = 0
tagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x00)
- )
+ )
+
+ _instance = None
+
+ def __new__(cls, *args, **kwargs):
+ if cls._instance is None:
+ cls._instance = object.__new__(cls, *args, **kwargs)
+
+ return cls._instance
+
+
endOfOctets = EndOfOctets()
diff -u -r 1.9.67/google_appengine/lib/pyasn1/pyasn1/codec/cer/decoder.py 1.9.68/google_appengine/lib/pyasn1/pyasn1/codec/cer/decoder.py
--- 1.9.67/google_appengine/lib/pyasn1/pyasn1/codec/cer/decoder.py 2018-03-09 20:05:30.000000000 -0800
+++ 1.9.68/google_appengine/lib/pyasn1/pyasn1/codec/cer/decoder.py 2018-04-03 21:35:14.000000000 -0700
@@ -1,13 +1,24 @@
-# CER decoder
-from pyasn1.type import univ
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2017, Ilya Etingof <[email protected]>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+from pyasn1 import error
from pyasn1.codec.ber import decoder
from pyasn1.compat.octets import oct2int
-from pyasn1 import error
+from pyasn1.type import univ
+
+__all__ = ['decode']
+
class BooleanDecoder(decoder.AbstractSimpleDecoder):
protoComponent = univ.Boolean(0)
- def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length,
- state, decodeFun, substrateFun):
+
+ def valueDecoder(self, substrate, asn1Spec,
+ tagSet=None, length=None, state=None,
+ decodeFun=None, substrateFun=None,
+ **options):
head, tail = substrate[:length], substrate[length:]
if not head or length != 1:
raise error.PyAsn1Error('Not single-octet Boolean payload')
@@ -21,15 +32,83 @@
value = 0
else:
raise error.PyAsn1Error('Unexpected Boolean payload: %s' % byte)
- return self._createComponent(asn1Spec, tagSet, value), tail
+ return self._createComponent(asn1Spec, tagSet, value, **options), tail
+
+# TODO: prohibit non-canonical encoding
+BitStringDecoder = decoder.BitStringDecoder
+OctetStringDecoder = decoder.OctetStringDecoder
+RealDecoder = decoder.RealDecoder
tagMap = decoder.tagMap.copy()
-tagMap.update({
- univ.Boolean.tagSet: BooleanDecoder()
- })
+tagMap.update(
+ {univ.Boolean.tagSet: BooleanDecoder(),
+ univ.BitString.tagSet: BitStringDecoder(),
+ univ.OctetString.tagSet: OctetStringDecoder(),
+ univ.Real.tagSet: RealDecoder()}
+)
+
+typeMap = decoder.typeMap.copy()
+
+# Put in non-ambiguous types for faster codec lookup
+for typeDecoder in tagMap.values():
+ if typeDecoder.protoComponent is not None:
+ typeId = typeDecoder.protoComponent.__class__.typeId
+ if typeId is not None and typeId not in typeMap:
+ typeMap[typeId] = typeDecoder
+
-typeMap = decoder.typeMap
+class Decoder(decoder.Decoder):
+ pass
-class Decoder(decoder.Decoder): pass
+#: Turns CER octet stream into an ASN.1 object.
+#:
+#: Takes CER octet-stream and decode it into an ASN.1 object
+#: (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative) which
+#: may be a scalar or an arbitrary nested structure.
+#:
+#: Parameters
+#: ----------
+#: substrate: :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2)
+#: CER octet-stream
+#:
+#: Keyword Args
+#: ------------
+#: asn1Spec: any pyasn1 type object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+#: A pyasn1 type object to act as a template guiding the decoder. Depending on the ASN.1 structure
+#: being decoded, *asn1Spec* may or may not be required. Most common reason for
+#: it to require is that ASN.1 structure is encoded in *IMPLICIT* tagging mode.
+#:
+#: Returns
+#: -------
+#: : :py:class:`tuple`
+#: A tuple of pyasn1 object recovered from CER substrate (:py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#: and the unprocessed trailing portion of the *substrate* (may be empty)
+#:
+#: Raises
+#: ------
+#: :py:class:`~pyasn1.error.PyAsn1Error`
+#: On decoding errors
+#:
+#: Examples
+#: --------
+#: Decode CER serialisation without ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#: >>> s, _ = decode(b'0\x80\x02\x01\x01\x02\x01\x02\x02\x01\x03\x00\x00')
+#: >>> str(s)
+#: SequenceOf:
+#: 1 2 3
+#:
+#: Decode CER serialisation with ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#: >>> seq = SequenceOf(componentType=Integer())
+#: >>> s, _ = decode(b'0\x80\x02\x01\x01\x02\x01\x02\x02\x01\x03\x00\x00', asn1Spec=seq)
+#: >>> str(s)
+#: SequenceOf:
+#: 1 2 3
+#:
decode = Decoder(tagMap, decoder.typeMap)
diff -u -r 1.9.67/google_appengine/lib/pyasn1/pyasn1/codec/cer/encoder.py 1.9.68/google_appengine/lib/pyasn1/pyasn1/codec/cer/encoder.py
--- 1.9.67/google_appengine/lib/pyasn1/pyasn1/codec/cer/encoder.py 2018-03-09 20:05:30.000000000 -0800
+++ 1.9.68/google_appengine/lib/pyasn1/pyasn1/codec/cer/encoder.py 2018-04-03 21:35:14.000000000 -0700
@@ -1,130 +1,296 @@
-# CER encoder
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2017, Ilya Etingof <[email protected]>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+from pyasn1 import error
+from pyasn1.codec.ber import encoder
+from pyasn1.compat.octets import str2octs, null
from pyasn1.type import univ
from pyasn1.type import useful
-from pyasn1.codec.ber import encoder
-from pyasn1.compat.octets import int2oct, str2octs, null
-from pyasn1 import error
+
+__all__ = ['encode']
+
class BooleanEncoder(encoder.IntegerEncoder):
- def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
- if client == 0:
- substrate = int2oct(0)
+ def encodeValue(self, value, asn1Spec, encodeFun, **options):
+ if value == 0:
+ substrate = (0,)
else:
- substrate = int2oct(255)
- return substrate, 0
-
-class BitStringEncoder(encoder.BitStringEncoder):
- def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
- return encoder.BitStringEncoder.encodeValue(
- self, encodeFun, client, defMode, 1000
- )
+ substrate = (255,)
+ return substrate, False, False
-class OctetStringEncoder(encoder.OctetStringEncoder):
- def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
- return encoder.OctetStringEncoder.encodeValue(
- self, encodeFun, client, defMode, 1000
- )
class RealEncoder(encoder.RealEncoder):
def _chooseEncBase(self, value):
m, b, e = value
return self._dropFloatingPoint(m, b, e)
+
# specialized GeneralStringEncoder here
-class GeneralizedTimeEncoder(OctetStringEncoder):
- zchar = str2octs('Z')
- pluschar = str2octs('+')
- minuschar = str2octs('-')
- zero = str2octs('0')
- def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
- octets = client.asOctets()
-# This breaks too many existing data items
-# if '.' not in octets:
-# raise error.PyAsn1Error('Format must include fraction of second: %r' % octets)
- if len(octets) < 15:
- raise error.PyAsn1Error('Bad UTC time length: %r' % octets)
- if self.pluschar in octets or self.minuschar in octets:
- raise error.PyAsn1Error('Must be UTC time: %r' % octets)
- if octets[-1] != self.zchar[0]:
- raise error.PyAsn1Error('Missing timezone specifier: %r' % octets)
- return encoder.OctetStringEncoder.encodeValue(
- self, encodeFun, client, defMode, 1000
- )
+class TimeEncoderMixIn(object):
+ zchar, = str2octs('Z')
+ pluschar, = str2octs('+')
+ minuschar, = str2octs('-')
+ commachar, = str2octs(',')
+ minLength = 12
+ maxLength = 19
+
+ def encodeValue(self, value, asn1Spec, encodeFun, **options):
+ # Encoding constraints:
+ # - minutes are mandatory, seconds are optional
+ # - subseconds must NOT be zero
+ # - no hanging fraction dot
+ # - time in UTC (Z)
+ # - only dot is allowed for fractions
+
+ if asn1Spec is not None:
+ value = asn1Spec.clone(value)
+
+ octets = value.asOctets()
+
+ if not self.minLength < len(octets) < self.maxLength:
+ raise error.PyAsn1Error('Length constraint violated: %r' % value)
-class UTCTimeEncoder(encoder.OctetStringEncoder):
- zchar = str2octs('Z')
- pluschar = str2octs('+')
- minuschar = str2octs('-')
- def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
- octets = client.asOctets()
if self.pluschar in octets or self.minuschar in octets:
raise error.PyAsn1Error('Must be UTC time: %r' % octets)
- if octets and octets[-1] != self.zchar[0]:
- client = client.clone(octets + self.zchar)
- if len(client) != 13:
- raise error.PyAsn1Error('Bad UTC time length: %r' % client)
+
+ if octets[-1] != self.zchar:
+ raise error.PyAsn1Error('Missing "Z" time zone specifier: %r' % octets)
+
+ if self.commachar in octets:
+ raise error.PyAsn1Error('Comma in fractions disallowed: %r' % value)
+
+ options.update(maxChunkSize=1000)
+
return encoder.OctetStringEncoder.encodeValue(
- self, encodeFun, client, defMode, 1000
+ self, value, asn1Spec, encodeFun, **options
)
-class SetOfEncoder(encoder.SequenceOfEncoder):
- def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
- if isinstance(client, univ.SequenceAndSetBase):
- client.setDefaultComponents()
- client.verifySizeSpec()
- substrate = null; idx = len(client)
- # This is certainly a hack but how else do I distinguish SetOf
- # from Set if they have the same tags&constraints?
- if isinstance(client, univ.SequenceAndSetBase):
- # Set
- comps = []
- while idx > 0:
- idx = idx - 1
- if client[idx] is None: # Optional component
+
+class GeneralizedTimeEncoder(TimeEncoderMixIn, encoder.OctetStringEncoder):
+ minLength = 12
+ maxLength = 19
+
+
+class UTCTimeEncoder(TimeEncoderMixIn, encoder.OctetStringEncoder):
+ minLength = 10
+ maxLength = 14
+
+
+class SetEncoder(encoder.SequenceEncoder):
+ @staticmethod
+ def _componentSortKey(componentAndType):
+ """Sort SET components by tag
+
+ Sort regardless of the Choice value (static sort)
+ """
+ component, asn1Spec = componentAndType
+
+ if asn1Spec is None:
+ asn1Spec = component
+
+ if asn1Spec.typeId == univ.Choice.typeId and not asn1Spec.tagSet:
+ if asn1Spec.tagSet:
+ return asn1Spec.tagSet
+ else:
+ return asn1Spec.componentType.minTagSet
+ else:
+ return asn1Spec.tagSet
+
+ def encodeValue(self, value, asn1Spec, encodeFun, **options):
+
+ substrate = null
+
+ comps = []
+ compsMap = {}
+
+ if asn1Spec is None:
+ # instance of ASN.1 schema
+ value.verifySizeSpec()
+
+ namedTypes = value.componentType
+
+ for idx, component in enumerate(value.values()):
+ if namedTypes:
+ namedType = namedTypes[idx]
+
+ if namedType.isOptional and not component.isValue:
+ continue
+
+ if namedType.isDefaulted and component == namedType.asn1Object:
+ continue
+
+ compsMap[id(component)] = namedType
+
+ else:
+ compsMap[id(component)] = None
+
+ comps.append((component, asn1Spec))
+
+ else:
+ # bare Python value + ASN.1 schema
+ for idx, namedType in enumerate(asn1Spec.componentType.namedTypes):
+
+ try:
+ component = value[namedType.name]
+
+ except KeyError:
+ raise error.PyAsn1Error('Component name "%s" not found in %r' % (namedType.name, value))
+
+ if namedType.isOptional and namedType.name not in value:
continue
- if client.getDefaultComponentByPosition(idx) == client[idx]:
+
+ if namedType.isDefaulted and component == namedType.asn1Object:
continue
- comps.append(client[idx])
- comps.sort(key=lambda x: isinstance(x, univ.Choice) and \
- x.getMinTagSet() or x.getTagSet())
- for c in comps:
- substrate += encodeFun(c, defMode, maxChunkSize)
+
+ compsMap[id(component)] = namedType
+ comps.append((component, asn1Spec[idx]))
+
+ for comp, compType in sorted(comps, key=self._componentSortKey):
+ namedType = compsMap[id(comp)]
+
+ if namedType:
+ options.update(ifNotEmpty=namedType.isOptional)
+
+ chunk = encodeFun(comp, compType, **options)
+
+ # wrap open type blob if needed
+ if namedType and namedType.openType:
+ wrapType = namedType.asn1Object
+ if wrapType.tagSet and not wrapType.isSameTypeWith(comp):
+ chunk = encodeFun(chunk, wrapType, **options)
+
+ substrate += chunk
+
+ return substrate, True, True
+
+
+class SetOfEncoder(encoder.SequenceOfEncoder):
+ def encodeValue(self, value, asn1Spec, encodeFun, **options):
+ if asn1Spec is None:
+ value.verifySizeSpec()
else:
- # SetOf
- compSubs = []
- while idx > 0:
- idx = idx - 1
- compSubs.append(
- encodeFun(client[idx], defMode, maxChunkSize)
- )
- compSubs.sort() # perhaps padding's not needed
- substrate = null
- for compSub in compSubs:
- substrate += compSub
- return substrate, 1
+ asn1Spec = asn1Spec.componentType
+
+ components = [encodeFun(x, asn1Spec, **options)
+ for x in value]
+
+ # sort by serialised and padded components
+ if len(components) > 1:
+ zero = str2octs('\x00')
+ maxLen = max(map(len, components))
+ paddedComponents = [
+ (x.ljust(maxLen, zero), x) for x in components
+ ]
+ paddedComponents.sort(key=lambda x: x[0])
+
+ components = [x[1] for x in paddedComponents]
+
+ substrate = null.join(components)
+
+ return substrate, True, True
+
+
+class SequenceEncoder(encoder.SequenceEncoder):
+ omitEmptyOptionals = True
+
+
+class SequenceOfEncoder(encoder.SequenceOfEncoder):
+ def encodeValue(self, value, asn1Spec, encodeFun, **options):
+
+ if options.get('ifNotEmpty', False) and not len(value):
+ return null, True, True
+
+ if asn1Spec is None:
+ value.verifySizeSpec()
+ else:
+ asn1Spec = asn1Spec.componentType
+
+ substrate = null
+
+ for idx, component in enumerate(value):
+ substrate += encodeFun(value[idx], asn1Spec, **options)
+
+ return substrate, True, True
+
tagMap = encoder.tagMap.copy()
tagMap.update({
univ.Boolean.tagSet: BooleanEncoder(),
- univ.BitString.tagSet: BitStringEncoder(),
- univ.OctetString.tagSet: OctetStringEncoder(),
univ.Real.tagSet: RealEncoder(),
useful.GeneralizedTime.tagSet: GeneralizedTimeEncoder(),
useful.UTCTime.tagSet: UTCTimeEncoder(),
- univ.SetOf().tagSet: SetOfEncoder() # conflcts with Set
+ # Sequence & Set have same tags as SequenceOf & SetOf
+ univ.SetOf.tagSet: SetOfEncoder(),
+ univ.Sequence.typeId: SequenceEncoder()
})
typeMap = encoder.typeMap.copy()
typeMap.update({
- univ.Set.typeId: SetOfEncoder(),
- univ.SetOf.typeId: SetOfEncoder()
+ univ.Boolean.typeId: BooleanEncoder(),
+ univ.Real.typeId: RealEncoder(),
+ useful.GeneralizedTime.typeId: GeneralizedTimeEncoder(),
+ useful.UTCTime.typeId: UTCTimeEncoder(),
+ # Sequence & Set have same tags as SequenceOf & SetOf
+ univ.Set.typeId: SetEncoder(),
+ univ.SetOf.typeId: SetOfEncoder(),
+ univ.Sequence.typeId: SequenceEncoder(),
+ univ.SequenceOf.typeId: SequenceOfEncoder()
})
+
class Encoder(encoder.Encoder):
- def __call__(self, client, defMode=False, maxChunkSize=0):
- return encoder.Encoder.__call__(self, client, defMode, maxChunkSize)
+ fixedDefLengthMode = False
+ fixedChunkSize = 1000
+#: Turns ASN.1 object into CER octet stream.
+#:
+#: Takes any ASN.1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#: walks all its components recursively and produces a CER octet stream.
+#:
+#: Parameters
+#: ----------
+#: value: either a Python or pyasn1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#: A Python or pyasn1 object to encode. If Python object is given, `asnSpec`
+#: parameter is required to guide the encoding process.
+#:
+#: Keyword Args
+#: ------------
+#: asn1Spec:
+#: Optional ASN.1 schema or value object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+#:
+#: Returns
+#: -------
+#: : :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2)
+#: Given ASN.1 object encoded into BER octet-stream
+#:
+#: Raises
+#: ------
+#: :py:class:`~pyasn1.error.PyAsn1Error`
+#: On encoding errors
+#:
+#: Examples
+#: --------
+#: Encode Python value into CER with ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#: >>> seq = SequenceOf(componentType=Integer())
+#: >>> encode([1, 2, 3], asn1Spec=seq)
+#: b'0\x80\x02\x01\x01\x02\x01\x02\x02\x01\x03\x00\x00'
+#:
+#: Encode ASN.1 value object into CER
+#:
+#: .. code-block:: pycon
+#:
+#: >>> seq = SequenceOf(componentType=Integer())
+#: >>> seq.extend([1, 2, 3])
+#: >>> encode(seq)
+#: b'0\x80\x02\x01\x01\x02\x01\x02\x02\x01\x03\x00\x00'
+#:
encode = Encoder(tagMap, typeMap)
# EncoderFactory queries class instance and builds a map of tags -> encoders
diff -u -r 1.9.67/google_appengine/lib/pyasn1/pyasn1/codec/der/decoder.py 1.9.68/google_appengine/lib/pyasn1/pyasn1/codec/der/decoder.py
--- 1.9.67/google_appengine/lib/pyasn1/pyasn1/codec/der/decoder.py 2018-03-09 20:05:30.000000000 -0800
+++ 1.9.68/google_appengine/lib/pyasn1/pyasn1/codec/der/decoder.py 2018-04-03 21:35:14.000000000 -0700
@@ -1,9 +1,94 @@
-# DER decoder
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2017, Ilya Etingof <[email protected]>
+# License: http://snmplabs.com/pyasn1/license.html
+#
from pyasn1.codec.cer import decoder
+from pyasn1.type import univ
+
+__all__ = ['decode']
+
+
+class BitStringDecoder(decoder.BitStringDecoder):
+ supportConstructedForm = False
+
+
+class OctetStringDecoder(decoder.OctetStringDecoder):
+ supportConstructedForm = False
+
+# TODO: prohibit non-canonical encoding
+RealDecoder = decoder.RealDecoder
+
+tagMap = decoder.tagMap.copy()
+tagMap.update(
+ {univ.BitString.tagSet: BitStringDecoder(),
+ univ.OctetString.tagSet: OctetStringDecoder(),
+ univ.Real.tagSet: RealDecoder()}
+)
+
+typeMap = decoder.typeMap.copy()
+
+# Put in non-ambiguous types for faster codec lookup
+for typeDecoder in tagMap.values():
+ if typeDecoder.protoComponent is not None:
+ typeId = typeDecoder.protoComponent.__class__.typeId
+ if typeId is not None and typeId not in typeMap:
+ typeMap[typeId] = typeDecoder
+
-tagMap = decoder.tagMap
-typeMap = decoder.typeMap
class Decoder(decoder.Decoder):
supportIndefLength = False
+
+#: Turns DER octet stream into an ASN.1 object.
+#:
+#: Takes DER octet-stream and decode it into an ASN.1 object
+#: (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative) which
+#: may be a scalar or an arbitrary nested structure.
+#:
+#: Parameters
+#: ----------
+#: substrate: :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2)
+#: DER octet-stream
+#:
+#: Keyword Args
+#: ------------
+#: asn1Spec: any pyasn1 type object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+#: A pyasn1 type object to act as a template guiding the decoder. Depending on the ASN.1 structure
+#: being decoded, *asn1Spec* may or may not be required. Most common reason for
+#: it to require is that ASN.1 structure is encoded in *IMPLICIT* tagging mode.
+#:
+#: Returns
+#: -------
+#: : :py:class:`tuple`
+#: A tuple of pyasn1 object recovered from DER substrate (:py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#: and the unprocessed trailing portion of the *substrate* (may be empty)
+#:
+#: Raises
+#: ------
+#: :py:class:`~pyasn1.error.PyAsn1Error`
+#: On decoding errors
+#:
+#: Examples
+#: --------
+#: Decode DER serialisation without ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#: >>> s, _ = decode(b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03')
+#: >>> str(s)
+#: SequenceOf:
+#: 1 2 3
+#:
+#: Decode DER serialisation with ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#: >>> seq = SequenceOf(componentType=Integer())
+#: >>> s, _ = decode(b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03', asn1Spec=seq)
+#: >>> str(s)
+#: SequenceOf:
+#: 1 2 3
+#:
decode = Decoder(tagMap, typeMap)
diff -u -r 1.9.67/google_appengine/lib/pyasn1/pyasn1/codec/der/encoder.py 1.9.68/google_appengine/lib/pyasn1/pyasn1/codec/der/encoder.py
--- 1.9.67/google_appengine/lib/pyasn1/pyasn1/codec/der/encoder.py 2018-03-09 20:05:30.000000000 -0800
+++ 1.9.68/google_appengine/lib/pyasn1/pyasn1/codec/der/encoder.py 2018-04-03 21:35:14.000000000 -0700
@@ -1,32 +1,107 @@
-# DER encoder
-from pyasn1.type import univ
-from pyasn1.codec.cer import encoder
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2017, Ilya Etingof <[email protected]>
+# License: http://snmplabs.com/pyasn1/license.html
+#
from pyasn1 import error
+from pyasn1.codec.cer import encoder
+from pyasn1.type import univ
+
+__all__ = ['encode']
+
+
+class SetEncoder(encoder.SetEncoder):
+ @staticmethod
+ def _componentSortKey(componentAndType):
+ """Sort SET components by tag
-class SetOfEncoder(encoder.SetOfEncoder):
- def _cmpSetComponents(self, c1, c2):
- tagSet1 = isinstance(c1, univ.Choice) and \
- c1.getEffectiveTagSet() or c1.getTagSet()
- tagSet2 = isinstance(c2, univ.Choice) and \
- c2.getEffectiveTagSet() or c2.getTagSet()
- return cmp(tagSet1, tagSet2)
+ Sort depending on the actual Choice value (dynamic sort)
+ """
+ component, asn1Spec = componentAndType
+
+ if asn1Spec is None:
+ compType = component
+ else:
+ compType = asn1Spec
+
+ if compType.typeId == univ.Choice.typeId and not compType.tagSet:
+ if asn1Spec is None:
+ return component.getComponent().tagSet
+ else:
+ # TODO: move out of sorting key function
+ names = [namedType.name for namedType in asn1Spec.componentType.namedTypes
+ if namedType.name in component]
+ if len(names) != 1:
+ raise error.PyAsn1Error(
+ '%s components for Choice at %r' % (len(names) and 'Multiple ' or 'None ', component))
+
+ # TODO: support nested CHOICE ordering
+ return asn1Spec[names[0]].tagSet
+
+ else:
+ return compType.tagSet
tagMap = encoder.tagMap.copy()
tagMap.update({
- # Overload CER encoders with BER ones (a bit hackerish XXX)
- univ.BitString.tagSet: encoder.encoder.BitStringEncoder(),
- univ.OctetString.tagSet: encoder.encoder.OctetStringEncoder(),
# Set & SetOf have same tags
- univ.SetOf().tagSet: SetOfEncoder()
+ univ.Set.tagSet: SetEncoder()
+})
+
+typeMap = encoder.typeMap.copy()
+typeMap.update({
+ # Set & SetOf have same tags
+ univ.Set.typeId: SetEncoder()
})
-typeMap = encoder.typeMap
class Encoder(encoder.Encoder):
- supportIndefLength = False
- def __call__(self, client, defMode=True, maxChunkSize=0):
- if not defMode:
- raise error.PyAsn1Error('DER forbids indefinite length mode')
- return encoder.Encoder.__call__(self, client, defMode, maxChunkSize)
+ fixedDefLengthMode = True
+ fixedChunkSize = 0
+#: Turns ASN.1 object into DER octet stream.
+#:
+#: Takes any ASN.1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#: walks all its components recursively and produces a DER octet stream.
+#:
+#: Parameters
+#: ----------
+#: value: either a Python or pyasn1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
+#: A Python or pyasn1 object to encode. If Python object is given, `asnSpec`
+#: parameter is required to guide the encoding process.
+#:
+#: Keyword Args
+#: ------------
+#: asn1Spec:
+#: Optional ASN.1 schema or value object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+#:
+#: Returns
+#: -------
+#: : :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2)
+#: Given ASN.1 object encoded into BER octet-stream
+#:
+#: Raises
+#: ------
+#: :py:class:`~pyasn1.error.PyAsn1Error`
+#: On encoding errors
+#:
+#: Examples
+#: --------
+#: Encode Python value into DER with ASN.1 schema
+#:
+#: .. code-block:: pycon
+#:
+#: >>> seq = SequenceOf(componentType=Integer())
+#: >>> encode([1, 2, 3], asn1Spec=seq)
+#: b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03'
+#:
+#: Encode ASN.1 value object into DER
+#:
+#: .. code-block:: pycon
+#:
+#: >>> seq = SequenceOf(componentType=Integer())
+#: >>> seq.extend([1, 2, 3])
+#: >>> encode(seq)
+#: b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03'
+#:
encode = Encoder(tagMap, typeMap)
Only in 1.9.68/google_appengine/lib/pyasn1/pyasn1/codec: native
Only in 1.9.68/google_appengine/lib/pyasn1/pyasn1/compat: binary.py
Only in 1.9.68/google_appengine/lib/pyasn1/pyasn1/compat: calling.py
Only in 1.9.68/google_appengine/lib/pyasn1/pyasn1/compat: dateandtime.py
Only in 1.9.68/google_appengine/lib/pyasn1/pyasn1/compat: integer.py
diff -u -r 1.9.67/google_appengine/lib/pyasn1/pyasn1/compat/octets.py 1.9.68/google_appengine/lib/pyasn1/pyasn1/compat/octets.py
--- 1.9.67/google_appengine/lib/pyasn1/pyasn1/compat/octets.py 2018-03-09 20:05:30.000000000 -0800
+++ 1.9.68/google_appengine/lib/pyasn1/pyasn1/compat/octets.py 2018-04-03 21:35:14.000000000 -0700
@@ -1,22 +1,46 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2017, Ilya Etingof <[email protected]>
+# License: http://snmplabs.com/pyasn1/license.html
+#
from sys import version_info
if version_info[0] <= 2:
int2oct = chr
- ints2octs = lambda s: ''.join([ int2oct(x) for x in s ])
+ # noinspection PyPep8
+ ints2octs = lambda s: ''.join([int2oct(x) for x in s])
null = ''
oct2int = ord
- octs2ints = lambda s: [ oct2int(x) for x in s ]
+ # TODO: refactor to return a sequence of ints
+ # noinspection PyPep8
+ octs2ints = lambda s: [oct2int(x) for x in s]
+ # noinspection PyPep8
str2octs = lambda x: x
+ # noinspection PyPep8
octs2str = lambda x: x
+ # noinspection PyPep8
isOctetsType = lambda s: isinstance(s, str)
+ # noinspection PyPep8
isStringType = lambda s: isinstance(s, (str, unicode))
+ # noinspection PyPep8
+ ensureString = str
else:
ints2octs = bytes
+ # noinspection PyPep8
int2oct = lambda x: ints2octs((x,))
null = ints2octs()
+ # noinspection PyPep8
oct2int = lambda x: x
- octs2ints = lambda s: [ x for x in s ]
- str2octs = lambda x: x.encode()
- octs2str = lambda x: x.decode()
+ # noinspection PyPep8
+ octs2ints = lambda x: x
+ # noinspection PyPep8
+ str2octs = lambda x: x.encode('iso-8859-1')
+ # noinspection PyPep8
+ octs2str = lambda x: x.decode('iso-8859-1')
+ # noinspection PyPep8
isOctetsType = lambda s: isinstance(s, bytes)
+ # noinspection PyPep8
isStringType = lambda s: isinstance(s, str)
+ # noinspection PyPep8
+ ensureString = bytes
Only in 1.9.68/google_appengine/lib/pyasn1/pyasn1/compat: string.py
diff -u -r 1.9.67/google_appengine/lib/pyasn1/pyasn1/debug.py 1.9.68/google_appengine/lib/pyasn1/pyasn1/debug.py
--- 1.9.67/google_appengine/lib/pyasn1/pyasn1/debug.py 2018-03-09 20:05:30.000000000 -0800
+++ 1.9.68/google_appengine/lib/pyasn1/pyasn1/debug.py 2018-04-03 21:35:14.000000000 -0700
@@ -1,79 +1,105 @@
-import time
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2017, Ilya Etingof <[email protected]>
+# License: http://snmplabs.com/pyasn1/license.html
+#
import logging
-from pyasn1.compat.octets import octs2ints
-from pyasn1 import error
+
from pyasn1 import __version__
+from pyasn1 import error
+from pyasn1.compat.octets import octs2ints
-flagNone = 0x0000
-flagEncoder = 0x0001
-flagDecoder = 0x0002
-flagAll = 0xffff
+__all__ = ['Debug', 'setLogger', 'hexdump']
+
+flagNone = 0x0000
+flagEncoder = 0x0001
+flagDecoder = 0x0002
+flagAll = 0xffff
flagMap = {
+ 'none': flagNone,
'encoder': flagEncoder,
'decoder': flagDecoder,
'all': flagAll
- }
+}
+
-class Printer:
+class Printer(object):
+ # noinspection PyShadowingNames
def __init__(self, logger=None, handler=None, formatter=None):
if logger is None:
logger = logging.getLogger('pyasn1')
+
logger.setLevel(logging.DEBUG)
+
if handler is None:
handler = logging.StreamHandler()
+
if formatter is None:
formatter = logging.Formatter('%(asctime)s %(name)s: %(message)s')
+
handler.setFormatter(formatter)
handler.setLevel(logging.DEBUG)
logger.addHandler(handler)
+
self.__logger = logger
- def __call__(self, msg): self.__logger.debug(msg)
- def __str__(self): return '<python built-in logging>'
+ def __call__(self, msg):
+ self.__logger.debug(msg)
+
+ def __str__(self):
+ return '<python logging>'
+
if hasattr(logging, 'NullHandler'):
NullHandler = logging.NullHandler
+
else:
# Python 2.6 and older
class NullHandler(logging.Handler):
def emit(self, record):
pass
-class Debug:
- defaultPrinter = None
+
+class Debug(object):
+ defaultPrinter = Printer()
+
def __init__(self, *flags, **options):
self._flags = flagNone
- if options.get('printer') is not None:
- self._printer = options.get('printer')
- elif self.defaultPrinter is not None:
- self._printer = self.defaultPrinter
- if 'loggerName' in options:
+
+ if 'loggerName' in options:
# route our logs to parent logger
self._printer = Printer(
logger=logging.getLogger(options['loggerName']),
handler=NullHandler()
)
+
+ elif 'printer' in options:
+ self._printer = options.get('printer')
+
else:
- self._printer = Printer()
- self('running pyasn1 version %s' % __version__)
- for f in flags:
- inverse = f and f[0] in ('!', '~')
+ self._printer = self.defaultPrinter
+
+ self._printer('running pyasn1 %s, debug flags %s' % (__version__, ', '.join(flags)))
+
+ for flag in flags:
+ inverse = flag and flag[0] in ('!', '~')
if inverse:
- f = f[1:]
+ flag = flag[1:]
try:
if inverse:
- self._flags &= ~flagMap[f]
+ self._flags &= ~flagMap[flag]
else:
- self._flags |= flagMap[f]
+ self._flags |= flagMap[flag]
except KeyError:
- raise error.PyAsn1Error('bad debug flag %s' % f)
-
- self('debug category \'%s\' %s' % (f, inverse and 'disabled' or 'enabled'))
+ raise error.PyAsn1Error('bad debug flag %s' % flag)
+
+ self._printer("debug category '%s' %s" % (flag, inverse and 'disabled' or 'enabled'))
def __str__(self):
return 'logger %s, flags %x' % (self._printer, self._flags)
-
+
def __call__(self, msg):
self._printer(msg)
@@ -83,19 +109,27 @@
def __rand__(self, flag):
return flag & self._flags
+
logger = 0
-def setLogger(l):
+
+def setLogger(userLogger):
global logger
- logger = l
+
+ if userLogger:
+ logger = userLogger
+ else:
+ logger = 0
+
def hexdump(octets):
return ' '.join(
- [ '%s%.2X' % (n%16 == 0 and ('\n%.5d: ' % n) or '', x)
- for n,x in zip(range(len(octets)), octs2ints(octets)) ]
- )
+ ['%s%.2X' % (n % 16 == 0 and ('\n%.5d: ' % n) or '', x)
+ for n, x in zip(range(len(octets)), octs2ints(octets))]
+ )
-class Scope:
+
+class Scope(object):
def __init__(self):
self._list = []
@@ -107,4 +141,5 @@
def pop(self):
return self._list.pop()
+
scope = Scope()
diff -u -r 1.9.67/google_appengine/lib/pyasn1/pyasn1/error.py 1.9.68/google_appengine/lib/pyasn1/pyasn1/error.py
--- 1.9.67/google_appengine/lib/pyasn1/pyasn1/error.py 2018-03-09 20:05:30.000000000 -0800
+++ 1.9.68/google_appengine/lib/pyasn1/pyasn1/error.py 2018-04-03 21:35:14.000000000 -0700
@@ -1,3 +1,29 @@
-class PyAsn1Error(Exception): pass
-class ValueConstraintError(PyAsn1Error): pass
-class SubstrateUnderrunError(PyAsn1Error): pass
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2017, Ilya Etingof <[email protected]>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+
+
+class PyAsn1Error(Exception):
+ """Create pyasn1 exception object
+
+ The `PyAsn1Error` exception represents generic, usually fatal, error.
+ """
+
+
+class ValueConstraintError(PyAsn1Error):
+ """Create pyasn1 exception object
+
+ The `ValueConstraintError` exception indicates an ASN.1 value
+ constraint violation.
+ """
+
+
+class SubstrateUnderrunError(PyAsn1Error):
+ """Create pyasn1 exception object
+
+ The `SubstrateUnderrunError` exception indicates insufficient serialised
+ data on input of a deserialisation routine.
+ """
diff -u -r 1.9.67/google_appengine/lib/pyasn1/pyasn1/type/base.py 1.9.68/google_appengine/lib/pyasn1/pyasn1/type/base.py
--- 1.9.67/google_appengine/lib/pyasn1/pyasn1/type/base.py 2018-03-09 20:05:30.000000000 -0800
+++ 1.9.68/google_appengine/lib/pyasn1/pyasn1/type/base.py 2018-04-03 21:35:14.000000000 -0700
@@ -1,151 +1,433 @@
-# Base classes for ASN.1 types
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2017, Ilya Etingof <[email protected]>
+# License: http://snmplabs.com/pyasn1/license.html
+#
import sys
-from pyasn1.type import constraint, tagmap, tag
+
from pyasn1 import error
+from pyasn1.compat import calling
+from pyasn1.type import constraint
+from pyasn1.type import tag
+from pyasn1.type import tagmap
+
+__all__ = ['Asn1Item', 'Asn1ItemBase', 'AbstractSimpleAsn1Item', 'AbstractConstructedAsn1Item']
+
+
+class Asn1Item(object):
+ @classmethod
+ def getTypeId(cls, increment=1):
+ try:
+ Asn1Item._typeCounter += increment
+ except AttributeError:
+ Asn1Item._typeCounter = increment
+ return Asn1Item._typeCounter
-class Asn1Item: pass
class Asn1ItemBase(Asn1Item):
- # Set of tags for this ASN.1 type
+ #: Set or return a :py:class:`~pyasn1.type.tag.TagSet` object representing
+ #: ASN.1 tag(s) associated with |ASN.1| type.
tagSet = tag.TagSet()
-
- # A list of constraint.Constraint instances for checking values
+
+ #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+ #: object imposing constraints on initialization values.
subtypeSpec = constraint.ConstraintsIntersection()
- # Used for ambiguous ASN.1 types identification
+ # Disambiguation ASN.1 types identification
typeId = None
-
- def __init__(self, tagSet=None, subtypeSpec=None):
- if tagSet is None:
- self._tagSet = self.tagSet
- else:
- self._tagSet = tagSet
- if subtypeSpec is None:
- self._subtypeSpec = self.subtypeSpec
- else:
- self._subtypeSpec = subtypeSpec
- def _verifySubtypeSpec(self, value, idx=None):
- try:
- self._subtypeSpec(value, idx)
- except error.PyAsn1Error:
- c, i, t = sys.exc_info()
- raise c('%s at %s' % (i, self.__class__.__name__))
-
- def getSubtypeSpec(self): return self._subtypeSpec
-
- def getTagSet(self): return self._tagSet
- def getEffectiveTagSet(self): return self._tagSet # used by untagged types
- def getTagMap(self): return tagmap.TagMap({self._tagSet: self})
-
+ def __init__(self, **kwargs):
+ readOnly = {
+ 'tagSet': self.tagSet,
+ 'subtypeSpec': self.subtypeSpec
+ }
+
+ readOnly.update(kwargs)
+
+ self.__dict__.update(readOnly)
+
+ self._readOnly = readOnly
+
+ def __setattr__(self, name, value):
+ if name[0] != '_' and name in self._readOnly:
+ raise error.PyAsn1Error('read-only instance attribute "%s"' % name)
+
+ self.__dict__[name] = value
+
+ def __str__(self):
+ return self.prettyPrint()
+
+ @property
+ def readOnly(self):
+ return self._readOnly
+
+ @property
+ def effectiveTagSet(self):
+ """For |ASN.1| type is equivalent to *tagSet*
+ """
+ return self.tagSet # used by untagged types
+
+ @property
+ def tagMap(self):
+ """Return a :class:`~pyasn1.type.tagmap.TagMap` object mapping ASN.1 tags to ASN.1 objects within callee object.
+ """
+ return tagmap.TagMap({self.tagSet: self})
+
def isSameTypeWith(self, other, matchTags=True, matchConstraints=True):
- return self is other or \
- (not matchTags or \
- self._tagSet == other.getTagSet()) and \
- (not matchConstraints or \
- self._subtypeSpec==other.getSubtypeSpec())
+ """Examine |ASN.1| type for equality with other ASN.1 type.
+
+ ASN.1 tags (:py:mod:`~pyasn1.type.tag`) and constraints
+ (:py:mod:`~pyasn1.type.constraint`) are examined when carrying
+ out ASN.1 types comparison.
+
+ Python class inheritance relationship is NOT considered.
+
+ Parameters
+ ----------
+ other: a pyasn1 type object
+ Class instance representing ASN.1 type.
+
+ Returns
+ -------
+ : :class:`bool`
+ :class:`True` if *other* is |ASN.1| type,
+ :class:`False` otherwise.
+ """
+ return (self is other or
+ (not matchTags or self.tagSet == other.tagSet) and
+ (not matchConstraints or self.subtypeSpec == other.subtypeSpec))
def isSuperTypeOf(self, other, matchTags=True, matchConstraints=True):
- """Returns true if argument is a ASN1 subtype of ourselves"""
- return (not matchTags or \
- self._tagSet.isSuperTagSetOf(other.getTagSet())) and \
- (not matchConstraints or \
- (self._subtypeSpec.isSuperTypeOf(other.getSubtypeSpec())))
+ """Examine |ASN.1| type for subtype relationship with other ASN.1 type.
+
+ ASN.1 tags (:py:mod:`~pyasn1.type.tag`) and constraints
+ (:py:mod:`~pyasn1.type.constraint`) are examined when carrying
+ out ASN.1 types comparison.
+
+ Python class inheritance relationship is NOT considered.
+
+ Parameters
+ ----------
+ other: a pyasn1 type object
+ Class instance representing ASN.1 type.
+
+ Returns
+ -------
+ : :class:`bool`
+ :class:`True` if *other* is a subtype of |ASN.1| type,
+ :class:`False` otherwise.
+ """
+ return (not matchTags or
+ (self.tagSet.isSuperTagSetOf(other.tagSet)) and
+ (not matchConstraints or self.subtypeSpec.isSuperTypeOf(other.subtypeSpec)))
+
+ @staticmethod
+ def isNoValue(*values):
+ for value in values:
+ if value is not noValue:
+ return False
+ return True
+
+ def prettyPrint(self, scope=0):
+ raise NotImplementedError()
+
+ # backward compatibility
+
+ def getTagSet(self):
+ return self.tagSet
+
+ def getEffectiveTagSet(self):
+ return self.effectiveTagSet
+
+ def getTagMap(self):
+ return self.tagMap
+
+ def getSubtypeSpec(self):
+ return self.subtypeSpec
+
+ def hasValue(self):
+ return self.isValue
+
+
+class NoValue(object):
+ """Create a singleton instance of NoValue class.
+
+ The *NoValue* sentinel object represents an instance of ASN.1 schema
+ object as opposed to ASN.1 value object.
+
+ Only ASN.1 schema-related operations can be performed on ASN.1
+ schema objects.
+
+ Warning
+ -------
+ Any operation attempted on the *noValue* object will raise the
+ *PyAsn1Error* exception.
+ """
+ skipMethods = set(
+ ('__slots__',
+ # attributes
+ '__getattribute__',
+ '__getattr__',
+ '__setattr__',
+ '__delattr__',
+ # class instance
+ '__class__',
+ '__init__',
+ '__del__',
+ '__new__',
+ '__repr__',
+ '__qualname__',
+ '__objclass__',
+ 'im_class',
+ '__sizeof__',
+ # pickle protocol
+ '__reduce__',
+ '__reduce_ex__',
+ '__getnewargs__',
+ '__getinitargs__',
+ '__getstate__',
+ '__setstate__')
+ )
+
+ _instance = None
+
+ def __new__(cls):
+ if cls._instance is None:
+ def getPlug(name):
+ def plug(self, *args, **kw):
+ raise error.PyAsn1Error('Attempted "%s" operation on ASN.1 schema object' % name)
+ return plug
+
+ op_names = [name
+ for typ in (str, int, list, dict)
+ for name in dir(typ)
+ if (name not in cls.skipMethods and
+ name.startswith('__') and
+ name.endswith('__') and
+ calling.callable(getattr(typ, name)))]
+
+ for name in set(op_names):
+ setattr(cls, name, getPlug(name))
+
+ cls._instance = object.__new__(cls)
+
+ return cls._instance
-class NoValue:
def __getattr__(self, attr):
- raise error.PyAsn1Error('No value for %s()' % attr)
- def __getitem__(self, i):
- raise error.PyAsn1Error('No value')
- def __repr__(self): return '%s()' % self.__class__.__name__
-
+ if attr in self.skipMethods:
+ raise AttributeError('Attribute %s not present' % attr)
+
+ raise error.PyAsn1Error('Attempted "%s" operation on ASN.1 schema object' % attr)
+
+ def __repr__(self):
+ return '<%s object at %s>' % (self.__class__.__name__, id(self))
+
+
noValue = NoValue()
+
# Base class for "simple" ASN.1 objects. These are immutable.
-class AbstractSimpleAsn1Item(Asn1ItemBase):
+class AbstractSimpleAsn1Item(Asn1ItemBase):
+ #: Default payload value
defaultValue = noValue
- def __init__(self, value=None, tagSet=None, subtypeSpec=None):
- Asn1ItemBase.__init__(self, tagSet, subtypeSpec)
- if value is None or value is noValue:
+
+ def __init__(self, value=noValue, **kwargs):
+ Asn1ItemBase.__init__(self, **kwargs)
+ if value is noValue:
value = self.defaultValue
- if value is None or value is noValue:
- self.__hashedValue = value = noValue
else:
value = self.prettyIn(value)
- self._verifySubtypeSpec(value)
- self.__hashedValue = hash(value)
+ try:
+ self.subtypeSpec(value)
+
+ except error.PyAsn1Error:
+ exType, exValue, exTb = sys.exc_info()
+ raise exType('%s at %s' % (exValue, self.__class__.__name__))
+
self._value = value
- self._len = None
-
+
def __repr__(self):
- r = []
- if self._value is not self.defaultValue:
- r.append(self.prettyOut(self._value))
- if self._tagSet is not self.tagSet:
- r.append('tagSet=%r' % (self._tagSet,))
- if self._subtypeSpec is not self.subtypeSpec:
- r.append('subtypeSpec=%r' % (self._subtypeSpec,))
- return '%s(%s)' % (self.__class__.__name__, ', '.join(r))
+ representation = '%s %s object at 0x%x' % (
+ self.__class__.__name__, self.isValue and 'value' or 'schema', id(self)
+ )
+
+ for attr, value in self.readOnly.items():
+ if value:
+ representation += ' %s %s' % (attr, value)
+
+ if self.isValue:
+ value = self.prettyPrint()
+ if len(value) > 32:
+ value = value[:16] + '...' + value[-16:]
+ representation += ' payload [%s]' % value
+
+ return '<%s>' % representation
- def __str__(self): return str(self._value)
def __eq__(self, other):
return self is other and True or self._value == other
- def __ne__(self, other): return self._value != other
- def __lt__(self, other): return self._value < other
- def __le__(self, other): return self._value <= other
- def __gt__(self, other): return self._value > other
- def __ge__(self, other): return self._value >= other
+
+ def __ne__(self, other):
+ return self._value != other
+
+ def __lt__(self, other):
+ return self._value < other
+
+ def __le__(self, other):
+ return self._value <= other
+
+ def __gt__(self, other):
+ return self._value > other
+
+ def __ge__(self, other):
+ return self._value >= other
+
if sys.version_info[0] <= 2:
- def __nonzero__(self): return bool(self._value)
+ def __nonzero__(self):
+ return self._value and True or False
else:
- def __bool__(self): return bool(self._value)
+ def __bool__(self):
+ return self._value and True or False
+
def __hash__(self):
- return self.__hashedValue is noValue and hash(noValue) or self.__hashedValue
+ return hash(self._value)
- def hasValue(self):
- return not isinstance(self._value, NoValue)
+ @property
+ def isValue(self):
+ """Indicate that |ASN.1| object represents ASN.1 value.
+
+ If *isValue* is `False` then this object represents just ASN.1 schema.
+
+ If *isValue* is `True` then, in addition to its ASN.1 schema features,
+ this object can also be used like a Python built-in object (e.g. `int`,
+ `str`, `dict` etc.).
+
+ Returns
+ -------
+ : :class:`bool`
+ :class:`False` if object represents just ASN.1 schema.
+ :class:`True` if object represents ASN.1 schema and can be used as a normal value.
+
+ Note
+ ----
+ There is an important distinction between PyASN1 schema and value objects.
+ The PyASN1 schema objects can only participate in ASN.1 schema-related
+ operations (e.g. defining or testing the structure of the data). Most
+ obvious uses of ASN.1 schema is to guide serialisation codecs whilst
+ encoding/decoding serialised ASN.1 contents.
+
+ The PyASN1 value objects can **additionally** participate in many operations
+ involving regular Python objects (e.g. arithmetic, comprehension etc).
+ """
+ return self._value is not noValue
+
+ def clone(self, value=noValue, **kwargs):
+ """Create a modified version of |ASN.1| schema or value object.
+
+ The `clone()` method accepts the same set arguments as |ASN.1|
+ class takes on instantiation except that all arguments
+ of the `clone()` method are optional.
+
+ Whatever arguments are supplied, they are used to create a copy
+ of `self` taking precedence over the ones used to instantiate `self`.
+
+ Note
+ ----
+ Due to the immutable nature of the |ASN.1| object, if no arguments
+ are supplied, no new |ASN.1| object will be created and `self` will
+ be returned instead.
+ """
+ if value is noValue:
+ if not kwargs:
+ return self
- def clone(self, value=None, tagSet=None, subtypeSpec=None):
- if value is None and tagSet is None and subtypeSpec is None:
- return self
- if value is None:
value = self._value
- if tagSet is None:
- tagSet = self._tagSet
- if subtypeSpec is None:
- subtypeSpec = self._subtypeSpec
- return self.__class__(value, tagSet, subtypeSpec)
-
- def subtype(self, value=None, implicitTag=None, explicitTag=None,
- subtypeSpec=None):
- if value is None:
+
+ initilaizers = self.readOnly.copy()
+ initilaizers.update(kwargs)
+
+ return self.__class__(value, **initilaizers)
+
+ def subtype(self, value=noValue, **kwargs):
+ """Create a specialization of |ASN.1| schema or value object.
+
+ The subtype relationship between ASN.1 types has no correlation with
+ subtype relationship between Python types. ASN.1 type is mainly identified
+ by its tag(s) (:py:class:`~pyasn1.type.tag.TagSet`) and value range
+ constraints (:py:class:`~pyasn1.type.constraint.ConstraintsIntersection`).
+ These ASN.1 type properties are implemented as |ASN.1| attributes.
+
+ The `subtype()` method accepts the same set arguments as |ASN.1|
+ class takes on instantiation except that all parameters
+ of the `subtype()` method are optional.
+
+ With the exception of the arguments described below, the rest of
+ supplied arguments they are used to create a copy of `self` taking
+ precedence over the ones used to instantiate `self`.
+
+ The following arguments to `subtype()` create a ASN.1 subtype out of
+ |ASN.1| type:
+
+ Other Parameters
+ ----------------
+ implicitTag: :py:class:`~pyasn1.type.tag.Tag`
+ Implicitly apply given ASN.1 tag object to `self`'s
+ :py:class:`~pyasn1.type.tag.TagSet`, then use the result as
+ new object's ASN.1 tag(s).
+
+ explicitTag: :py:class:`~pyasn1.type.tag.Tag`
+ Explicitly apply given ASN.1 tag object to `self`'s
+ :py:class:`~pyasn1.type.tag.TagSet`, then use the result as
+ new object's ASN.1 tag(s).
+
+ subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+ Add ASN.1 constraints object to one of the `self`'s, then
+ use the result as new object's ASN.1 constraints.
+
+ Returns
+ -------
+ :
+ new instance of |ASN.1| schema or value object
+
+ Note
+ ----
+ Due to the immutable nature of the |ASN.1| object, if no arguments
+ are supplied, no new |ASN.1| object will be created and `self` will
+ be returned instead.
+ """
+ if value is noValue:
+ if not kwargs:
+ return self
+
value = self._value
+
+ initializers = self.readOnly.copy()
+
+ implicitTag = kwargs.pop('implicitTag', None)
if implicitTag is not None:
- tagSet = self._tagSet.tagImplicitly(implicitTag)
- elif explicitTag is not None:
- tagSet = self._tagSet.tagExplicitly(explicitTag)
- else:
- tagSet = self._tagSet
- if subtypeSpec is None:
- subtypeSpec = self._subtypeSpec
- else:
- subtypeSpec = subtypeSpec + self._subtypeSpec
- return self.__class__(value, tagSet, subtypeSpec)
+ initializers['tagSet'] = self.tagSet.tagImplicitly(implicitTag)
+
+ explicitTag = kwargs.pop('explicitTag', None)
+ if explicitTag is not None:
+ initializers['tagSet'] = self.tagSet.tagExplicitly(explicitTag)
+
+ for arg, option in kwargs.items():
+ initializers[arg] += option
+
+ return self.__class__(value, **initializers)
- def prettyIn(self, value): return value
- def prettyOut(self, value): return str(value)
+ def prettyIn(self, value):
+ return value
+
+ def prettyOut(self, value):
+ return str(value)
def prettyPrint(self, scope=0):
- if self.hasValue():
- return self.prettyOut(self._value)
- else:
- return '<no value>'
+ return self.prettyOut(self._value)
- # XXX Compatibility stub
- def prettyPrinter(self, scope=0): return self.prettyPrint(scope)
-
+ # noinspection PyUnusedLocal
def prettyPrintType(self, scope=0):
- return '%s -> %s' % (self.getTagSet(), self.__class__.__name__)
+ return '%s -> %s' % (self.tagSet, self.__class__.__name__)
#
# Constructed types:
@@ -166,113 +448,196 @@
# of types for Sequence/Set/Choice.
#
+
class AbstractConstructedAsn1Item(Asn1ItemBase):
+
+ #: If `True`, requires exact component type matching,
+ #: otherwise subtype relation is only enforced
+ strictConstraints = False
+
componentType = None
- sizeSpec = constraint.ConstraintsIntersection()
- def __init__(self, componentType=None, tagSet=None,
- subtypeSpec=None, sizeSpec=None):
- Asn1ItemBase.__init__(self, tagSet, subtypeSpec)
- if componentType is None:
- self._componentType = self.componentType
- else:
- self._componentType = componentType
- if sizeSpec is None:
- self._sizeSpec = self.sizeSpec
- else:
- self._sizeSpec = sizeSpec
+ sizeSpec = None
+
+ def __init__(self, **kwargs):
+ readOnly = {
+ 'componentType': self.componentType,
+ 'sizeSpec': self.sizeSpec
+ }
+ readOnly.update(kwargs)
+
+ Asn1ItemBase.__init__(self, **readOnly)
+
self._componentValues = []
- self._componentValuesSet = 0
def __repr__(self):
- r = []
- if self._componentType is not self.componentType:
- r.append('componentType=%r' % (self._componentType,))
- if self._tagSet is not self.tagSet:
- r.append('tagSet=%r' % (self._tagSet,))
- if self._subtypeSpec is not self.subtypeSpec:
- r.append('subtypeSpec=%r' % (self._subtypeSpec,))
- r = '%s(%s)' % (self.__class__.__name__, ', '.join(r))
- if self._componentValues:
- r += '.setComponents(%s)' % ', '.join([repr(x) for x in self._componentValues])
- return r
+ representation = '%s %s object at 0x%x' % (
+ self.__class__.__name__, self.isValue and 'value' or 'schema', id(self)
+ )
+
+ for attr, value in self.readOnly.items():
+ if value is not noValue:
+ representation += ' %s=%r' % (attr, value)
+
+ if self.isValue and self._componentValues:
+ representation += ' payload [%s]' % ', '.join([repr(x) for x in self._componentValues])
+
+ return '<%s>' % representation
def __eq__(self, other):
return self is other and True or self._componentValues == other
- def __ne__(self, other): return self._componentValues != other
- def __lt__(self, other): return self._componentValues < other
- def __le__(self, other): return self._componentValues <= other
- def __gt__(self, other): return self._componentValues > other
- def __ge__(self, other): return self._componentValues >= other
+
+ def __ne__(self, other):
+ return self._componentValues != other
+
+ def __lt__(self, other):
+ return self._componentValues < other
+
+ def __le__(self, other):
+ return self._componentValues <= other
+
+ def __gt__(self, other):
+ return self._componentValues > other
+
+ def __ge__(self, other):
+ return self._componentValues >= other
+
if sys.version_info[0] <= 2:
- def __nonzero__(self): return bool(self._componentValues)
+ def __nonzero__(self):
+ return self._componentValues and True or False
else:
- def __bool__(self): return bool(self._componentValues)
+ def __bool__(self):
+ return self._componentValues and True or False
- def getComponentTagMap(self):
- raise error.PyAsn1Error('Method not implemented')
+ def __len__(self):
+ return len(self._componentValues)
+
+ def _cloneComponentValues(self, myClone, cloneValueFlag):
+ pass
+
+ def clone(self, **kwargs):
+ """Create a modified version of |ASN.1| schema object.
+
+ The `clone()` method accepts the same set arguments as |ASN.1|
+ class takes on instantiation except that all arguments
+ of the `clone()` method are optional.
+
+ Whatever arguments are supplied, they are used to create a copy
+ of `self` taking precedence over the ones used to instantiate `self`.
+
+ Possible values of `self` are never copied over thus `clone()` can
+ only create a new schema object.
+
+ Returns
+ -------
+ :
+ new instance of |ASN.1| type/value
- def _cloneComponentValues(self, myClone, cloneValueFlag): pass
+ Note
+ ----
+ Due to the mutable nature of the |ASN.1| object, even if no arguments
+ are supplied, new |ASN.1| object will always be created as a shallow
+ copy of `self`.
+ """
+ cloneValueFlag = kwargs.pop('cloneValueFlag', False)
+
+ initilaizers = self.readOnly.copy()
+ initilaizers.update(kwargs)
+
+ clone = self.__class__(**initilaizers)
- def clone(self, tagSet=None, subtypeSpec=None, sizeSpec=None,
- cloneValueFlag=None):
- if tagSet is None:
- tagSet = self._tagSet
- if subtypeSpec is None:
- subtypeSpec = self._subtypeSpec
- if sizeSpec is None:
- sizeSpec = self._sizeSpec
- r = self.__class__(self._componentType, tagSet, subtypeSpec, sizeSpec)
if cloneValueFlag:
- self._cloneComponentValues(r, cloneValueFlag)
- return r
+ self._cloneComponentValues(clone, cloneValueFlag)
+
+ return clone
+
+ def subtype(self, **kwargs):
+ """Create a specialization of |ASN.1| schema object.
+
+ The `subtype()` method accepts the same set arguments as |ASN.1|
+ class takes on instantiation except that all parameters
+ of the `subtype()` method are optional.
+
+ With the exception of the arguments described below, the rest of
+ supplied arguments they are used to create a copy of `self` taking
+ precedence over the ones used to instantiate `self`.
+
+ The following arguments to `subtype()` create a ASN.1 subtype out of
+ |ASN.1| type.
+
+ Other Parameters
+ ----------------
+ implicitTag: :py:class:`~pyasn1.type.tag.Tag`
+ Implicitly apply given ASN.1 tag object to `self`'s
+ :py:class:`~pyasn1.type.tag.TagSet`, then use the result as
+ new object's ASN.1 tag(s).
+
+ explicitTag: :py:class:`~pyasn1.type.tag.Tag`
+ Explicitly apply given ASN.1 tag object to `self`'s
+ :py:class:`~pyasn1.type.tag.TagSet`, then use the result as
+ new object's ASN.1 tag(s).
+
+ subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+ Add ASN.1 constraints object to one of the `self`'s, then
+ use the result as new object's ASN.1 constraints.
+
+
+ Returns
+ -------
+ :
+ new instance of |ASN.1| type/value
+
+ Note
+ ----
+ Due to the immutable nature of the |ASN.1| object, if no arguments
+ are supplied, no new |ASN.1| object will be created and `self` will
+ be returned instead.
+ """
+
+ initializers = self.readOnly.copy()
- def subtype(self, implicitTag=None, explicitTag=None, subtypeSpec=None,
- sizeSpec=None, cloneValueFlag=None):
+ cloneValueFlag = kwargs.pop('cloneValueFlag', False)
+
+ implicitTag = kwargs.pop('implicitTag', None)
if implicitTag is not None:
- tagSet = self._tagSet.tagImplicitly(implicitTag)
- elif explicitTag is not None:
- tagSet = self._tagSet.tagExplicitly(explicitTag)
- else:
- tagSet = self._tagSet
- if subtypeSpec is None:
- subtypeSpec = self._subtypeSpec
- else:
- subtypeSpec = subtypeSpec + self._subtypeSpec
- if sizeSpec is None:
- sizeSpec = self._sizeSpec
- else:
- sizeSpec = sizeSpec + self._sizeSpec
- r = self.__class__(self._componentType, tagSet, subtypeSpec, sizeSpec)
+ initializers['tagSet'] = self.tagSet.tagImplicitly(implicitTag)
+
+ explicitTag = kwargs.pop('explicitTag', None)
+ if explicitTag is not None:
+ initializers['tagSet'] = self.tagSet.tagExplicitly(explicitTag)
+
+ for arg, option in kwargs.items():
+ initializers[arg] += option
+
+ clone = self.__class__(**initializers)
+
if cloneValueFlag:
- self._cloneComponentValues(r, cloneValueFlag)
- return r
+ self._cloneComponentValues(clone, cloneValueFlag)
- def _verifyComponent(self, idx, value): pass
+ return clone
- def verifySizeSpec(self): self._sizeSpec(self)
+ def verifySizeSpec(self):
+ self.sizeSpec(self)
def getComponentByPosition(self, idx):
raise error.PyAsn1Error('Method not implemented')
+
def setComponentByPosition(self, idx, value, verifyConstraints=True):
raise error.PyAsn1Error('Method not implemented')
def setComponents(self, *args, **kwargs):
- for idx in range(len(args)):
- self[idx] = args[idx]
+ for idx, value in enumerate(args):
+ self[idx] = value
for k in kwargs:
self[k] = kwargs[k]
return self
- def getComponentType(self): return self._componentType
-
- def setDefaultComponents(self): pass
-
- def __getitem__(self, idx): return self.getComponentByPosition(idx)
- def __setitem__(self, idx, value): self.setComponentByPosition(idx, value)
-
- def __len__(self): return len(self._componentValues)
-
def clear(self):
self._componentValues = []
- self._componentValuesSet = 0
+ # backward compatibility
+
+ def setDefaultComponents(self):
+ pass
+
+ def getComponentType(self):
+ return self.componentType
diff -u -r 1.9.67/google_appengine/lib/pyasn1/pyasn1/type/char.py 1.9.68/google_appengine/lib/pyasn1/pyasn1/type/char.py
--- 1.9.67/google_appengine/lib/pyasn1/pyasn1/type/char.py 2018-03-09 20:05:30.000000000 -0800
+++ 1.9.68/google_appengine/lib/pyasn1/pyasn1/type/char.py 2018-04-03 21:35:14.000000000 -0700
@@ -1,64 +1,321 @@
-# ASN.1 "character string" types
-from pyasn1.type import univ, tag
-
-class NumericString(univ.OctetString):
- tagSet = univ.OctetString.tagSet.tagImplicitly(
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2017, Ilya Etingof <[email protected]>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import sys
+
+from pyasn1 import error
+from pyasn1.type import tag
+from pyasn1.type import univ
+
+__all__ = ['NumericString', 'PrintableString', 'TeletexString', 'T61String', 'VideotexString',
+ 'IA5String', 'GraphicString', 'VisibleString', 'ISO646String',
+ 'GeneralString', 'UniversalString', 'BMPString', 'UTF8String']
+
+NoValue = univ.NoValue
+noValue = univ.noValue
+
+
+class AbstractCharacterString(univ.OctetString):
+ """Creates |ASN.1| schema or value object.
+
+ |ASN.1| objects are immutable and duck-type Python 2 :class:`unicode` or Python 3 :class:`str`.
+ When used in octet-stream context, |ASN.1| type assumes "|encoding|" encoding.
+
+ Keyword Args
+ ------------
+ value: :class:`unicode`, :class:`str`, :class:`bytes` or |ASN.1| object
+ unicode object (Python 2) or string (Python 3), alternatively string
+ (Python 2) or bytes (Python 3) representing octet-stream of serialised
+ unicode string (note `encoding` parameter) or |ASN.1| class instance.
+
+ tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+ Object representing non-default ASN.1 tag(s)
+
+ subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+ Object representing non-default ASN.1 subtype constraint(s)
+
+ encoding: :py:class:`str`
+ Unicode codec ID to encode/decode :class:`unicode` (Python 2) or
+ :class:`str` (Python 3) the payload when |ASN.1| object is used
+ in octet-stream context.
+
+ Raises
+ ------
+ :py:class:`~pyasn1.error.PyAsn1Error`
+ On constraint violation or bad initializer.
+ """
+
+ if sys.version_info[0] <= 2:
+ def __str__(self):
+ try:
+ # `str` is Py2 text representation
+ return self._value.encode(self.encoding)
+
+ except UnicodeEncodeError:
+ raise error.PyAsn1Error(
+ "Can't encode string '%s' with codec %s" % (self._value, self.encoding)
+ )
+
+ def __unicode__(self):
+ return unicode(self._value)
+
+ def prettyIn(self, value):
+ try:
+ if isinstance(value, unicode):
+ return value
+ elif isinstance(value, str):
+ return value.decode(self.encoding)
+ elif isinstance(value, (tuple, list)):
+ return self.prettyIn(''.join([chr(x) for x in value]))
+ elif isinstance(value, univ.OctetString):
+ return value.asOctets().decode(self.encoding)
+ else:
+ return unicode(value)
+
+ except (UnicodeDecodeError, LookupError):
+ raise error.PyAsn1Error(
+ "Can't decode string '%s' with codec %s" % (value, self.encoding)
+ )
+
+ def asOctets(self, padding=True):
+ return str(self)
+
+ def asNumbers(self, padding=True):
+ return tuple([ord(x) for x in str(self)])
+
+ else:
+ def __str__(self):
+ # `unicode` is Py3 text representation
+ return str(self._value)
+
+ def __bytes__(self):
+ try:
+ return self._value.encode(self.encoding)
+ except UnicodeEncodeError:
+ raise error.PyAsn1Error(
+ "Can't encode string '%s' with codec %s" % (self._value, self.encoding)
+ )
+
+ def prettyIn(self, value):
+ try:
+ if isinstance(value, str):
+ return value
+ elif isinstance(value, bytes):
+ return value.decode(self.encoding)
+ elif isinstance(value, (tuple, list)):
+ return self.prettyIn(bytes(value))
+ elif isinstance(value, univ.OctetString):
+ return value.asOctets().decode(self.encoding)
+ else:
+ return str(value)
+
+ except (UnicodeDecodeError, LookupError):
+ raise error.PyAsn1Error(
+ "Can't decode string '%s' with codec %s" % (value, self.encoding)
+ )
+
+ def asOctets(self, padding=True):
+ return bytes(self)
+
+ def asNumbers(self, padding=True):
+ return tuple(bytes(self))
+
+ #
+ # See OctetString.prettyPrint() for the explanation
+ #
+
+ def prettyOut(self, value):
+ return value
+
+ def prettyPrint(self, scope=0):
+ # first see if subclass has its own .prettyOut()
+ value = self.prettyOut(self._value)
+
+ if value is not self._value:
+ return value
+
+ return AbstractCharacterString.__str__(self)
+
+ def __reversed__(self):
+ return reversed(self._value)
+
+
+class NumericString(AbstractCharacterString):
+ __doc__ = AbstractCharacterString.__doc__
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+ #: associated with |ASN.1| type.
+ tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 18)
- )
+ )
+ encoding = 'us-ascii'
+
+ # Optimization for faster codec lookup
+ typeId = AbstractCharacterString.getTypeId()
+
-class PrintableString(univ.OctetString):
- tagSet = univ.OctetString.tagSet.tagImplicitly(
+class PrintableString(AbstractCharacterString):
+ __doc__ = AbstractCharacterString.__doc__
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+ #: associated with |ASN.1| type.
+ tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 19)
- )
+ )
+ encoding = 'us-ascii'
+
+ # Optimization for faster codec lookup
+ typeId = AbstractCharacterString.getTypeId()
-class TeletexString(univ.OctetString):
- tagSet = univ.OctetString.tagSet.tagImplicitly(
+
+class TeletexString(AbstractCharacterString):
+ __doc__ = AbstractCharacterString.__doc__
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+ #: associated with |ASN.1| type.
+ tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 20)
- )
+ )
+ encoding = 'iso-8859-1'
+
+ # Optimization for faster codec lookup
+ typeId = AbstractCharacterString.getTypeId()
+
-class T61String(TeletexString): pass
+class T61String(TeletexString):
+ __doc__ = TeletexString.__doc__
-class VideotexString(univ.OctetString):
- tagSet = univ.OctetString.tagSet.tagImplicitly(
+ # Optimization for faster codec lookup
+ typeId = AbstractCharacterString.getTypeId()
+
+
+class VideotexString(AbstractCharacterString):
+ __doc__ = AbstractCharacterString.__doc__
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+ #: associated with |ASN.1| type.
+ tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 21)
- )
+ )
+ encoding = 'iso-8859-1'
+
+ # Optimization for faster codec lookup
+ typeId = AbstractCharacterString.getTypeId()
+
-class IA5String(univ.OctetString):
- tagSet = univ.OctetString.tagSet.tagImplicitly(
+class IA5String(AbstractCharacterString):
+ __doc__ = AbstractCharacterString.__doc__
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+ #: associated with |ASN.1| type.
+ tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 22)
- )
+ )
+ encoding = 'us-ascii'
+
+ # Optimization for faster codec lookup
+ typeId = AbstractCharacterString.getTypeId()
+
+
+class GraphicString(AbstractCharacterString):
+ __doc__ = AbstractCharacterString.__doc__
-class GraphicString(univ.OctetString):
- tagSet = univ.OctetString.tagSet.tagImplicitly(
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+ #: associated with |ASN.1| type.
+ tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 25)
- )
+ )
+ encoding = 'iso-8859-1'
-class VisibleString(univ.OctetString):
- tagSet = univ.OctetString.tagSet.tagImplicitly(
+ # Optimization for faster codec lookup
+ typeId = AbstractCharacterString.getTypeId()
+
+
+class VisibleString(AbstractCharacterString):
+ __doc__ = AbstractCharacterString.__doc__
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+ #: associated with |ASN.1| type.
+ tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 26)
- )
+ )
+ encoding = 'us-ascii'
+
+ # Optimization for faster codec lookup
+ typeId = AbstractCharacterString.getTypeId()
+
-class ISO646String(VisibleString): pass
+class ISO646String(VisibleString):
+ __doc__ = VisibleString.__doc__
-class GeneralString(univ.OctetString):
- tagSet = univ.OctetString.tagSet.tagImplicitly(
+ # Optimization for faster codec lookup
+ typeId = AbstractCharacterString.getTypeId()
+
+class GeneralString(AbstractCharacterString):
+ __doc__ = AbstractCharacterString.__doc__
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+ #: associated with |ASN.1| type.
+ tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 27)
- )
+ )
+ encoding = 'iso-8859-1'
+
+ # Optimization for faster codec lookup
+ typeId = AbstractCharacterString.getTypeId()
+
-class UniversalString(univ.OctetString):
- tagSet = univ.OctetString.tagSet.tagImplicitly(
+class UniversalString(AbstractCharacterString):
+ __doc__ = AbstractCharacterString.__doc__
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+ #: associated with |ASN.1| type.
+ tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 28)
- )
+ )
encoding = "utf-32-be"
-class BMPString(univ.OctetString):
- tagSet = univ.OctetString.tagSet.tagImplicitly(
+ # Optimization for faster codec lookup
+ typeId = AbstractCharacterString.getTypeId()
+
+
+class BMPString(AbstractCharacterString):
+ __doc__ = AbstractCharacterString.__doc__
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+ #: associated with |ASN.1| type.
+ tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 30)
- )
+ )
encoding = "utf-16-be"
-class UTF8String(univ.OctetString):
- tagSet = univ.OctetString.tagSet.tagImplicitly(
+ # Optimization for faster codec lookup
+ typeId = AbstractCharacterString.getTypeId()
+
+
+class UTF8String(AbstractCharacterString):
+ __doc__ = AbstractCharacterString.__doc__
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+ #: associated with |ASN.1| type.
+ tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12)
- )
+ )
encoding = "utf-8"
+
+ # Optimization for faster codec lookup
+ typeId = AbstractCharacterString.getTypeId()
diff -u -r 1.9.67/google_appengine/lib/pyasn1/pyasn1/type/constraint.py 1.9.68/google_appengine/lib/pyasn1/pyasn1/type/constraint.py
--- 1.9.67/google_appengine/lib/pyasn1/pyasn1/type/constraint.py 2018-03-09 20:05:30.000000000 -0800
+++ 1.9.68/google_appengine/lib/pyasn1/pyasn1/type/constraint.py 2018-04-03 21:35:14.000000000 -0700
@@ -1,86 +1,222 @@
#
-# ASN.1 subtype constraints classes.
+# This file is part of pyasn1 software.
#
-# Constraints are relatively rare, but every ASN1 object
-# is doing checks all the time for whether they have any
-# constraints and whether they are applicable to the object.
+# Copyright (c) 2005-2017, Ilya Etingof <[email protected]>
+# License: http://snmplabs.com/pyasn1/license.html
#
-# What we're going to do is define objects/functions that
-# can be called unconditionally if they are present, and that
-# are simply not present if there are no constraints.
-#
-# Original concept and code by Mike C. Fletcher.
+# Original concept and code by Mike C. Fletcher.
#
import sys
+
from pyasn1.type import error
-class AbstractConstraint:
- """Abstract base-class for constraint objects
+__all__ = ['SingleValueConstraint', 'ContainedSubtypeConstraint',
+ 'ValueRangeConstraint', 'ValueSizeConstraint',
+ 'PermittedAlphabetConstraint', 'InnerTypeConstraint',
+ 'ConstraintsExclusion', 'ConstraintsIntersection',
+ 'ConstraintsUnion']
+
+
+class AbstractConstraint(object):
- Constraints should be stored in a simple sequence in the
- namespace of their client Asn1Item sub-classes.
- """
def __init__(self, *values):
- self._valueMap = {}
+ self._valueMap = set()
self._setValues(values)
- self.__hashedValues = None
+ self.__hash = hash((self.__class__.__name__, self._values))
+
def __call__(self, value, idx=None):
+ if not self._values:
+ return
+
try:
self._testValue(value, idx)
+
except error.ValueConstraintError:
raise error.ValueConstraintError(
- '%s failed at: \"%s\"' % (self, sys.exc_info()[1])
+ '%s failed at: %r' % (self, sys.exc_info()[1])
)
+
def __repr__(self):
- return '%s(%s)' % (
- self.__class__.__name__,
- ', '.join([repr(x) for x in self._values])
- )
+ representation = '%s object at 0x%x' % (self.__class__.__name__, id(self))
+
+ if self._values:
+ representation += ' consts %s' % ', '.join([repr(x) for x in self._values])
+
+ return '<%s>' % representation
+
def __eq__(self, other):
return self is other and True or self._values == other
- def __ne__(self, other): return self._values != other
- def __lt__(self, other): return self._values < other
- def __le__(self, other): return self._values <= other
- def __gt__(self, other): return self._values > other
- def __ge__(self, other): return self._values >= other
+
+ def __ne__(self, other):
+ return self._values != other
+
+ def __lt__(self, other):
+ return self._values < other
+
+ def __le__(self, other):
+ return self._values <= other
+
+ def __gt__(self, other):
+ return self._values > other
+
+ def __ge__(self, other):
+ return self._values >= other
+
if sys.version_info[0] <= 2:
- def __nonzero__(self): return bool(self._values)
+ def __nonzero__(self):
+ return self._values and True or False
else:
- def __bool__(self): return bool(self._values)
+ def __bool__(self):
+ return self._values and True or False
def __hash__(self):
- if self.__hashedValues is None:
- self.__hashedValues = hash((self.__class__.__name__, self._values))
- return self.__hashedValues
+ return self.__hash
+
+ def _setValues(self, values):
+ self._values = values
- def _setValues(self, values): self._values = values
def _testValue(self, value, idx):
raise error.ValueConstraintError(value)
# Constraints derivation logic
- def getValueMap(self): return self._valueMap
+ def getValueMap(self):
+ return self._valueMap
+
def isSuperTypeOf(self, otherConstraint):
- return self in otherConstraint.getValueMap() or \
- otherConstraint is self or otherConstraint == self
+ # TODO: fix possible comparison of set vs scalars here
+ return (otherConstraint is self or
+ not self._values or
+ otherConstraint == self or
+ self in otherConstraint.getValueMap())
+
def isSubTypeOf(self, otherConstraint):
- return otherConstraint in self._valueMap or \
- otherConstraint is self or otherConstraint == self
+ return (otherConstraint is self or
+ not self or
+ otherConstraint == self or
+ otherConstraint in self._valueMap)
+
class SingleValueConstraint(AbstractConstraint):
- """Value must be part of defined values constraint"""
+ """Create a SingleValueConstraint object.
+
+ The SingleValueConstraint satisfies any value that
+ is present in the set of permitted values.
+
+ The SingleValueConstraint object can be applied to
+ any ASN.1 type.
+
+ Parameters
+ ----------
+ \*values: :class:`int`
+ Full set of values permitted by this constraint object.
+
+ Examples
+ --------
+ .. code-block:: python
+
+ class DivisorOfSix(Integer):
+ '''
+ ASN.1 specification:
+
+ Divisor-Of-6 ::= INTEGER (1 | 2 | 3 | 6)
+ '''
+ subtypeSpec = SingleValueConstraint(1, 2, 3, 6)
+
+ # this will succeed
+ divisor_of_six = DivisorOfSix(1)
+
+ # this will raise ValueConstraintError
+ divisor_of_six = DivisorOfSix(7)
+ """
+ def _setValues(self, values):
+ self._values = values
+ self._set = set(values)
+
def _testValue(self, value, idx):
- # XXX index vals for performance?
- if value not in self._values:
+ if value not in self._set:
raise error.ValueConstraintError(value)
+
class ContainedSubtypeConstraint(AbstractConstraint):
- """Value must satisfy all of defined set of constraints"""
+ """Create a ContainedSubtypeConstraint object.
+
+ The ContainedSubtypeConstraint satisfies any value that
+ is present in the set of permitted values and also
+ satisfies included constraints.
+
+ The ContainedSubtypeConstraint object can be applied to
+ any ASN.1 type.
+
+ Parameters
+ ----------
+ \*values:
+ Full set of values and constraint objects permitted
+ by this constraint object.
+
+ Examples
+ --------
+ .. code-block:: python
+
+ class DivisorOfEighteen(Integer):
+ '''
+ ASN.1 specification:
+
+ Divisors-of-18 ::= INTEGER (INCLUDES Divisors-of-6 | 9 | 18)
+ '''
+ subtypeSpec = ContainedSubtypeConstraint(
+ SingleValueConstraint(1, 2, 3, 6), 9, 18
+ )
+
+ # this will succeed
+ divisor_of_eighteen = DivisorOfEighteen(9)
+
+ # this will raise ValueConstraintError
+ divisor_of_eighteen = DivisorOfEighteen(10)
+ """
def _testValue(self, value, idx):
- for c in self._values:
- c(value, idx)
+ for constraint in self._values:
+ if isinstance(constraint, AbstractConstraint):
+ constraint(value, idx)
+ elif value not in self._set:
+ raise error.ValueConstraintError(value)
+
class ValueRangeConstraint(AbstractConstraint):
- """Value must be within start and stop values (inclusive)"""
+ """Create a ValueRangeConstraint object.
+
+ The ValueRangeConstraint satisfies any value that
+ falls in the range of permitted values.
+
+ The ValueRangeConstraint object can only be applied
+ to :class:`~pyasn1.type.univ.Integer` and
+ :class:`~pyasn1.type.univ.Real` types.
+
+ Parameters
+ ----------
+ start: :class:`int`
+ Minimum permitted value in the range (inclusive)
+
+ end: :class:`int`
+ Maximum permitted value in the range (inclusive)
+
+ Examples
+ --------
+ .. code-block:: python
+
+ class TeenAgeYears(Integer):
+ '''
+ ASN.1 specification:
+
+ TeenAgeYears ::= INTEGER (13 .. 19)
+ '''
+ subtypeSpec = ValueRangeConstraint(13, 19)
+
+ # this will succeed
+ teen_year = TeenAgeYears(18)
+
+ # this will raise ValueConstraintError
+ teen_year = TeenAgeYears(20)
+ """
def _testValue(self, value, idx):
if value < self.start or value > self.stop:
raise error.ValueConstraintError(value)
@@ -89,7 +225,7 @@
if len(values) != 2:
raise error.PyAsn1Error(
'%s: bad constraint values' % (self.__class__.__name__,)
- )
+ )
self.start, self.stop = values
if self.start > self.stop:
raise error.PyAsn1Error(
@@ -99,28 +235,116 @@
)
)
AbstractConstraint._setValues(self, values)
-
+
+
class ValueSizeConstraint(ValueRangeConstraint):
- """len(value) must be within start and stop values (inclusive)"""
+ """Create a ValueSizeConstraint object.
+
+ The ValueSizeConstraint satisfies any value for
+ as long as its size falls within the range of
+ permitted sizes.
+
+ The ValueSizeConstraint object can be applied
+ to :class:`~pyasn1.type.univ.BitString`,
+ :class:`~pyasn1.type.univ.OctetString` (including
+ all :ref:`character ASN.1 types <type.char>`),
+ :class:`~pyasn1.type.univ.SequenceOf`
+ and :class:`~pyasn1.type.univ.SetOf` types.
+
+ Parameters
+ ----------
+ minimum: :class:`int`
+ Minimum permitted size of the value (inclusive)
+
+ maximum: :class:`int`
+ Maximum permitted size of the value (inclusive)
+
+ Examples
+ --------
+ .. code-block:: python
+
+ class BaseballTeamRoster(SetOf):
+ '''
+ ASN.1 specification:
+
+ BaseballTeamRoster ::= SET SIZE (1..25) OF PlayerNames
+ '''
+ componentType = PlayerNames()
+ subtypeSpec = ValueSizeConstraint(1, 25)
+
+ # this will succeed
+ team = BaseballTeamRoster()
+ team.extend(['Jan', 'Matej'])
+ encode(team)
+
+ # this will raise ValueConstraintError
+ team = BaseballTeamRoster()
+ team.extend(['Jan'] * 26)
+ encode(team)
+
+ Note
+ ----
+ Whenever ValueSizeConstraint is applied to mutable types
+ (e.g. :class:`~pyasn1.type.univ.SequenceOf`,
+ :class:`~pyasn1.type.univ.SetOf`), constraint
+ validation only happens at the serialisation phase rather
+ than schema instantiation phase (as it is with immutable
+ types).
+ """
def _testValue(self, value, idx):
- l = len(value)
- if l < self.start or l > self.stop:
+ valueSize = len(value)
+ if valueSize < self.start or valueSize > self.stop:
raise error.ValueConstraintError(value)
+
class PermittedAlphabetConstraint(SingleValueConstraint):
+ """Create a PermittedAlphabetConstraint object.
+
+ The PermittedAlphabetConstraint satisfies any character
+ string for as long as all its characters are present in
+ the set of permitted characters.
+
+ The PermittedAlphabetConstraint object can only be applied
+ to the :ref:`character ASN.1 types <type.char>` such as
+ :class:`~pyasn1.type.char.IA5String`.
+
+ Parameters
+ ----------
+ \*alphabet: :class:`str`
+ Full set of characters permitted by this constraint object.
+
+ Examples
+ --------
+ .. code-block:: python
+
+ class BooleanValue(IA5String):
+ '''
+ ASN.1 specification:
+
+ BooleanValue ::= IA5String (FROM ('T' | 'F'))
+ '''
+ subtypeSpec = PermittedAlphabetConstraint('T', 'F')
+
+ # this will succeed
+ truth = BooleanValue('T')
+ truth = BooleanValue('TF')
+
+ # this will raise ValueConstraintError
+ garbage = BooleanValue('TAF')
+ """
def _setValues(self, values):
- self._values = ()
- for v in values:
- self._values = self._values + tuple(v)
+ self._values = values
+ self._set = set(values)
def _testValue(self, value, idx):
- for v in value:
- if v not in self._values:
- raise error.ValueConstraintError(value)
+ if not self._set.issuperset(value):
+ raise error.ValueConstraintError(value)
-# This is a bit kludgy, meaning two op modes within a single constraing
+
+# This is a bit kludgy, meaning two op modes within a single constraint
class InnerTypeConstraint(AbstractConstraint):
- """Value must satisfy type and presense constraints"""
+ """Value must satisfy the type and presence constraints"""
+
def _testValue(self, value, idx):
if self.__singleTypeConstraint:
self.__singleTypeConstraint(value)
@@ -128,7 +352,7 @@
if idx not in self.__multipleTypeConstraint:
raise error.ValueConstraintError(value)
constraint, status = self.__multipleTypeConstraint[idx]
- if status == 'ABSENT': # XXX presense is not checked!
+ if status == 'ABSENT': # XXX presense is not checked!
raise error.ValueConstraintError(value)
constraint(value)
@@ -142,10 +366,51 @@
self.__singleTypeConstraint = v
AbstractConstraint._setValues(self, values)
-# Boolean ops on constraints
+
+# Logic operations on constraints
class ConstraintsExclusion(AbstractConstraint):
- """Value must not fit the single constraint"""
+ """Create a ConstraintsExclusion logic operator object.
+
+ The ConstraintsExclusion logic operator succeeds when the
+ value does *not* satisfy the operand constraint.
+
+ The ConstraintsExclusion object can be applied to
+ any constraint and logic operator object.
+
+ Parameters
+ ----------
+ constraint:
+ Constraint or logic operator object.
+
+ Examples
+ --------
+ .. code-block:: python
+
+ class Lipogramme(IA5STRING):
+ '''
+ ASN.1 specification:
+
+ Lipogramme ::=
+ IA5String (FROM (ALL EXCEPT ("e"|"E")))
+ '''
+ subtypeSpec = ConstraintsExclusion(
+ PermittedAlphabetConstraint('e', 'E')
+ )
+
+ # this will succeed
+ lipogramme = Lipogramme('A work of fiction?')
+
+ # this will raise ValueConstraintError
+ lipogramme = Lipogramme('Eel')
+
+ Warning
+ -------
+ The above example involving PermittedAlphabetConstraint might
+ not work due to the way how PermittedAlphabetConstraint works.
+ The other constraints might work with ConstraintsExclusion
+ though.
+ """
def _testValue(self, value, idx):
try:
self._values[0](value, idx)
@@ -157,44 +422,135 @@
def _setValues(self, values):
if len(values) != 1:
raise error.PyAsn1Error('Single constraint expected')
+
AbstractConstraint._setValues(self, values)
+
class AbstractConstraintSet(AbstractConstraint):
- """Value must not satisfy the single constraint"""
- def __getitem__(self, idx): return self._values[idx]
- def __add__(self, value): return self.__class__(self, value)
- def __radd__(self, value): return self.__class__(self, value)
+ def __getitem__(self, idx):
+ return self._values[idx]
+
+ def __iter__(self):
+ return iter(self._values)
+
+ def __add__(self, value):
+ return self.__class__(*(self._values + (value,)))
- def __len__(self): return len(self._values)
+ def __radd__(self, value):
+ return self.__class__(*((value,) + self._values))
+
+ def __len__(self):
+ return len(self._values)
# Constraints inclusion in sets
-
+
def _setValues(self, values):
self._values = values
- for v in values:
- self._valueMap[v] = 1
- self._valueMap.update(v.getValueMap())
+ for constraint in values:
+ if constraint:
+ self._valueMap.add(constraint)
+ self._valueMap.update(constraint.getValueMap())
+
class ConstraintsIntersection(AbstractConstraintSet):
- """Value must satisfy all constraints"""
+ """Create a ConstraintsIntersection logic operator object.
+
+ The ConstraintsIntersection logic operator only succeeds
+ if *all* its operands succeed.
+
+ The ConstraintsIntersection object can be applied to
+ any constraint and logic operator objects.
+
+ The ConstraintsIntersection object duck-types the immutable
+ container object like Python :py:class:`tuple`.
+
+ Parameters
+ ----------
+ \*constraints:
+ Constraint or logic operator objects.
+
+ Examples
+ --------
+ .. code-block:: python
+
+ class CapitalAndSmall(IA5String):
+ '''
+ ASN.1 specification:
+
+ CapitalAndSmall ::=
+ IA5String (FROM ("A".."Z"|"a".."z"))
+ '''
+ subtypeSpec = ConstraintsIntersection(
+ PermittedAlphabetConstraint('A', 'Z'),
+ PermittedAlphabetConstraint('a', 'z')
+ )
+
+ # this will succeed
+ capital_and_small = CapitalAndSmall('Hello')
+
+ # this will raise ValueConstraintError
+ capital_and_small = CapitalAndSmall('hello')
+ """
def _testValue(self, value, idx):
- for v in self._values:
- v(value, idx)
+ for constraint in self._values:
+ constraint(value, idx)
+
class ConstraintsUnion(AbstractConstraintSet):
- """Value must satisfy at least one constraint"""
+ """Create a ConstraintsUnion logic operator object.
+
+ The ConstraintsUnion logic operator only succeeds if
+ *at least a single* operand succeeds.
+
+ The ConstraintsUnion object can be applied to
+ any constraint and logic operator objects.
+
+ The ConstraintsUnion object duck-types the immutable
+ container object like Python :py:class:`tuple`.
+
+ Parameters
+ ----------
+ \*constraints:
+ Constraint or logic operator objects.
+
+ Examples
+ --------
+ .. code-block:: python
+
+ class CapitalOrSmall(IA5String):
+ '''
+ ASN.1 specification:
+
+ CapitalOrSmall ::=
+ IA5String (FROM ("A".."Z") | FROM ("a".."z"))
+ '''
+ subtypeSpec = ConstraintsIntersection(
+ PermittedAlphabetConstraint('A', 'Z'),
+ PermittedAlphabetConstraint('a', 'z')
+ )
+
+ # this will succeed
+ capital_or_small = CapitalAndSmall('Hello')
+
+ # this will raise ValueConstraintError
+ capital_or_small = CapitalOrSmall('hello!')
+ """
def _testValue(self, value, idx):
- for v in self._values:
+ for constraint in self._values:
try:
- v(value, idx)
+ constraint(value, idx)
except error.ValueConstraintError:
pass
else:
return
+
raise error.ValueConstraintError(
- 'all of %s failed for \"%s\"' % (self._values, value)
- )
+ 'all of %s failed for "%s"' % (self._values, value)
+ )
-# XXX
+# TODO:
+# refactor InnerTypeConstraint
# add tests for type check
+# implement other constraint types
+# make constraint validation easy to skip
diff -u -r 1.9.67/google_appengine/lib/pyasn1/pyasn1/type/error.py 1.9.68/google_appengine/lib/pyasn1/pyasn1/type/error.py
--- 1.9.67/google_appengine/lib/pyasn1/pyasn1/type/error.py 2018-03-09 20:05:30.000000000 -0800
+++ 1.9.68/google_appengine/lib/pyasn1/pyasn1/type/error.py 2018-04-03 21:35:14.000000000 -0700
@@ -1,3 +1,11 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2017, Ilya Etingof <[email protected]>
+# License: http://snmplabs.com/pyasn1/license.html
+#
from pyasn1.error import PyAsn1Error
-class ValueConstraintError(PyAsn1Error): pass
+
+class ValueConstraintError(PyAsn1Error):
+ pass
diff -u -r 1.9.67/google_appengine/lib/pyasn1/pyasn1/type/namedtype.py 1.9.68/google_appengine/lib/pyasn1/pyasn1/type/namedtype.py
--- 1.9.67/google_appengine/lib/pyasn1/pyasn1/type/namedtype.py 2018-03-09 20:05:30.000000000 -0800
+++ 1.9.68/google_appengine/lib/pyasn1/pyasn1/type/namedtype.py 2018-04-03 21:35:14.000000000 -0700
@@ -1,149 +1,559 @@
-# NamedType specification for constructed types
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2017, Ilya Etingof <[email protected]>
+# License: http://snmplabs.com/pyasn1/license.html
+#
import sys
-from pyasn1.type import tagmap
+
from pyasn1 import error
+from pyasn1.type import tag
+from pyasn1.type import tagmap
+
+__all__ = ['NamedType', 'OptionalNamedType', 'DefaultedNamedType',
+ 'NamedTypes']
+
+try:
+ any
+
+except NameError:
+ any = lambda x: bool(filter(bool, x))
+
+
+class NamedType(object):
+ """Create named field object for a constructed ASN.1 type.
+
+ The |NamedType| object represents a single name and ASN.1 type of a constructed ASN.1 type.
+
+ |NamedType| objects are immutable and duck-type Python :class:`tuple` objects
+ holding *name* and *asn1Object* components.
+
+ Parameters
+ ----------
+ name: :py:class:`str`
+ Field name
+
+ asn1Object:
+ ASN.1 type object
+ """
+ isOptional = False
+ isDefaulted = False
+
+ def __init__(self, name, asn1Object, openType=None):
+ self.__name = name
+ self.__type = asn1Object
+ self.__nameAndType = name, asn1Object
+ self.__openType = openType
+
+ def __repr__(self):
+ representation = '%s=%r' % (self.name, self.asn1Object)
+
+ if self.openType:
+ representation += ' openType: %r' % self.openType
+
+ return '<%s object at 0x%x type %s>' % (self.__class__.__name__, id(self), representation)
+
+ def __eq__(self, other):
+ return self.__nameAndType == other
+
+ def __ne__(self, other):
+ return self.__nameAndType != other
+
+ def __lt__(self, other):
+ return self.__nameAndType < other
+
+ def __le__(self, other):
+ return self.__nameAndType <= other
+
+ def __gt__(self, other):
+ return self.__nameAndType > other
+
+ def __ge__(self, other):
+ return self.__nameAndType >= other
+
+ def __hash__(self):
+ return hash(self.__nameAndType)
-class NamedType:
- isOptional = 0
- isDefaulted = 0
- def __init__(self, name, t):
- self.__name = name; self.__type = t
- def __repr__(self): return '%s(%r, %r)' % (
- self.__class__.__name__, self.__name, self.__type
- )
- def __eq__(self, other): return tuple(self) == tuple(other)
- def __ne__(self, other): return tuple(self) != tuple(other)
- def __lt__(self, other): return tuple(self) < tuple(other)
- def __le__(self, other): return tuple(self) <= tuple(other)
- def __gt__(self, other): return tuple(self) > tuple(other)
- def __ge__(self, other): return tuple(self) >= tuple(other)
- def __hash__(self): return hash(tuple(self))
-
- def getType(self): return self.__type
- def getName(self): return self.__name
def __getitem__(self, idx):
- if idx == 0: return self.__name
- if idx == 1: return self.__type
- raise IndexError()
-
+ return self.__nameAndType[idx]
+
+ def __iter__(self):
+ return iter(self.__nameAndType)
+
+ @property
+ def name(self):
+ return self.__name
+
+ @property
+ def asn1Object(self):
+ return self.__type
+
+ @property
+ def openType(self):
+ return self.__openType
+
+ # Backward compatibility
+
+ def getName(self):
+ return self.name
+
+ def getType(self):
+ return self.asn1Object
+
+
class OptionalNamedType(NamedType):
- isOptional = 1
+ __doc__ = NamedType.__doc__
+
+ isOptional = True
+
+
class DefaultedNamedType(NamedType):
- isDefaulted = 1
-
-class NamedTypes:
- def __init__(self, *namedTypes):
+ __doc__ = NamedType.__doc__
+
+ isDefaulted = True
+
+
+class NamedTypes(object):
+ """Create a collection of named fields for a constructed ASN.1 type.
+
+ The NamedTypes object represents a collection of named fields of a constructed ASN.1 type.
+
+ *NamedTypes* objects are immutable and duck-type Python :class:`dict` objects
+ holding *name* as keys and ASN.1 type object as values.
+
+ Parameters
+ ----------
+ *namedTypes: :class:`~pyasn1.type.namedtype.NamedType`
+
+ Examples
+ --------
+
+ .. code-block:: python
+
+ class Description(Sequence):
+ '''
+ ASN.1 specification:
+
+ Description ::= SEQUENCE {
+ surname IA5String,
+ first-name IA5String OPTIONAL,
+ age INTEGER DEFAULT 40
+ }
+ '''
+ componentType = NamedTypes(
+ NamedType('surname', IA5String()),
+ OptionalNamedType('first-name', IA5String()),
+ DefaultedNamedType('age', Integer(40))
+ )
+
+ descr = Description()
+ descr['surname'] = 'Smith'
+ descr['first-name'] = 'John'
+ """
+ def __init__(self, *namedTypes, **kwargs):
self.__namedTypes = namedTypes
self.__namedTypesLen = len(self.__namedTypes)
- self.__minTagSet = None
- self.__tagToPosIdx = {}; self.__nameToPosIdx = {}
- self.__tagMap = { False: None, True: None }
- self.__ambigiousTypes = {}
+ self.__minTagSet = self.__computeMinTagSet()
+ self.__nameToPosMap = self.__computeNameToPosMap()
+ self.__tagToPosMap = self.__computeTagToPosMap()
+ self.__ambiguousTypes = 'terminal' not in kwargs and self.__computeAmbiguousTypes() or {}
+ self.__uniqueTagMap = self.__computeTagMaps(unique=True)
+ self.__nonUniqueTagMap = self.__computeTagMaps(unique=False)
+ self.__hasOptionalOrDefault = any([True for namedType in self.__namedTypes
+ if namedType.isDefaulted or namedType.isOptional])
+ self.__hasOpenTypes = any([True for namedType in self.__namedTypes
+ if namedType.openType])
+
+ self.__requiredComponents = frozenset(
+ [idx for idx, nt in enumerate(self.__namedTypes) if not nt.isOptional and not nt.isDefaulted]
+ )
+ self.__keys = frozenset([namedType.name for namedType in self.__namedTypes])
+ self.__values = tuple([namedType.asn1Object for namedType in self.__namedTypes])
+ self.__items = tuple([(namedType.name, namedType.asn1Object) for namedType in self.__namedTypes])
def __repr__(self):
- return '%s(%s)' % (
- self.__class__.__name__,
- ', '.join([ repr(x) for x in self.__namedTypes ])
- )
- def __eq__(self, other): return tuple(self) == tuple(other)
- def __ne__(self, other): return tuple(self) != tuple(other)
- def __lt__(self, other): return tuple(self) < tuple(other)
- def __le__(self, other): return tuple(self) <= tuple(other)
- def __gt__(self, other): return tuple(self) > tuple(other)
- def __ge__(self, other): return tuple(self) >= tuple(other)
- def __hash__(self): return hash(tuple(self))
-
- def __getitem__(self, idx): return self.__namedTypes[idx]
+ representation = ', '.join(['%r' % x for x in self.__namedTypes])
+ return '<%s object at 0x%x types %s>' % (self.__class__.__name__, id(self), representation)
+
+ def __eq__(self, other):
+ return self.__namedTypes == other
+
+ def __ne__(self, other):
+ return self.__namedTypes != other
+
+ def __lt__(self, other):
+ return self.__namedTypes < other
+
+ def __le__(self, other):
+ return self.__namedTypes <= other
+
+ def __gt__(self, other):
+ return self.__namedTypes > other
+
+ def __ge__(self, other):
+ return self.__namedTypes >= other
+
+ def __hash__(self):
+ return hash(self.__namedTypes)
+
+ def __getitem__(self, idx):
+ try:
+ return self.__namedTypes[idx]
+
+ except TypeError:
+ return self.__namedTypes[self.__nameToPosMap[idx]]
+
+ def __contains__(self, key):
+ return key in self.__nameToPosMap
+
+ def __iter__(self):
+ return (x[0] for x in self.__namedTypes)
if sys.version_info[0] <= 2:
- def __nonzero__(self): return bool(self.__namedTypesLen)
+ def __nonzero__(self):
+ return self.__namedTypesLen > 0
else:
- def __bool__(self): return bool(self.__namedTypesLen)
- def __len__(self): return self.__namedTypesLen
-
- def clone(self): return self.__class__(*self.__namedTypes)
-
+ def __bool__(self):
+ return self.__namedTypesLen > 0
+
+ def __len__(self):
+ return self.__namedTypesLen
+
+ # Python dict protocol
+
+ def values(self):
+ return self.__values
+
+ def keys(self):
+ return self.__keys
+
+ def items(self):
+ return self.__items
+
+ def clone(self):
+ return self.__class__(*self.__namedTypes)
+
+ class PostponedError(object):
+ def __init__(self, errorMsg):
+ self.__errorMsg = errorMsg
+
+ def __getitem__(self, item):
+ raise error.PyAsn1Error(self.__errorMsg)
+
+ def __computeTagToPosMap(self):
+ tagToPosMap = {}
+ for idx, namedType in enumerate(self.__namedTypes):
+ tagMap = namedType.asn1Object.tagMap
+ if isinstance(tagMap, NamedTypes.PostponedError):
+ return tagMap
+ if not tagMap:
+ continue
+ for _tagSet in tagMap.presentTypes:
+ if _tagSet in tagToPosMap:
+ return NamedTypes.PostponedError('Duplicate component tag %s at %s' % (_tagSet, namedType))
+ tagToPosMap[_tagSet] = idx
+
+ return tagToPosMap
+
+ def __computeNameToPosMap(self):
+ nameToPosMap = {}
+ for idx, namedType in enumerate(self.__namedTypes):
+ if namedType.name in nameToPosMap:
+ return NamedTypes.PostponedError('Duplicate component name %s at %s' % (namedType.name, namedType))
+ nameToPosMap[namedType.name] = idx
+
+ return nameToPosMap
+
+ def __computeAmbiguousTypes(self):
+ ambigiousTypes = {}
+ partialAmbigiousTypes = ()
+ for idx, namedType in reversed(tuple(enumerate(self.__namedTypes))):
+ if namedType.isOptional or namedType.isDefaulted:
+ partialAmbigiousTypes = (namedType,) + partialAmbigiousTypes
+ else:
+ partialAmbigiousTypes = (namedType,)
+ if len(partialAmbigiousTypes) == len(self.__namedTypes):
+ ambigiousTypes[idx] = self
+ else:
+ ambigiousTypes[idx] = NamedTypes(*partialAmbigiousTypes, **dict(terminal=True))
+ return ambigiousTypes
+
def getTypeByPosition(self, idx):
- if idx < 0 or idx >= self.__namedTypesLen:
+ """Return ASN.1 type object by its position in fields set.
+
+ Parameters
+ ----------
+ idx: :py:class:`int`
+ Field index
+
+ Returns
+ -------
+ :
+ ASN.1 type
+
+ Raises
+ ------
+ : :class:`~pyasn1.error.PyAsn1Error`
+ If given position is out of fields range
+ """
+ try:
+ return self.__namedTypes[idx].asn1Object
+
+ except IndexError:
raise error.PyAsn1Error('Type position out of range')
- else:
- return self.__namedTypes[idx].getType()
def getPositionByType(self, tagSet):
- if not self.__tagToPosIdx:
- idx = self.__namedTypesLen
- while idx > 0:
- idx = idx - 1
- tagMap = self.__namedTypes[idx].getType().getTagMap()
- for t in tagMap.getPosMap():
- if t in self.__tagToPosIdx:
- raise error.PyAsn1Error('Duplicate type %s' % (t,))
- self.__tagToPosIdx[t] = idx
+ """Return field position by its ASN.1 type.
+
+ Parameters
+ ----------
+ tagSet: :class:`~pysnmp.type.tag.TagSet`
+ ASN.1 tag set distinguishing one ASN.1 type from others.
+
+ Returns
+ -------
+ : :py:class:`int`
+ ASN.1 type position in fields set
+
+ Raises
+ ------
+ : :class:`~pyasn1.error.PyAsn1Error`
+ If *tagSet* is not present or ASN.1 types are not unique within callee *NamedTypes*
+ """
try:
- return self.__tagToPosIdx[tagSet]
+ return self.__tagToPosMap[tagSet]
+
except KeyError:
raise error.PyAsn1Error('Type %s not found' % (tagSet,))
-
+
def getNameByPosition(self, idx):
+ """Return field name by its position in fields set.
+
+ Parameters
+ ----------
+ idx: :py:class:`idx`
+ Field index
+
+ Returns
+ -------
+ : :py:class:`str`
+ Field name
+
+ Raises
+ ------
+ : :class:`~pyasn1.error.PyAsn1Error`
+ If given field name is not present in callee *NamedTypes*
+ """
try:
- return self.__namedTypes[idx].getName()
+ return self.__namedTypes[idx].name
+
except IndexError:
raise error.PyAsn1Error('Type position out of range')
+
def getPositionByName(self, name):
- if not self.__nameToPosIdx:
- idx = self.__namedTypesLen
- while idx > 0:
- idx = idx - 1
- n = self.__namedTypes[idx].getName()
- if n in self.__nameToPosIdx:
- raise error.PyAsn1Error('Duplicate name %s' % (n,))
- self.__nameToPosIdx[n] = idx
+ """Return field position by filed name.
+
+ Parameters
+ ----------
+ name: :py:class:`str`
+ Field name
+
+ Returns
+ -------
+ : :py:class:`int`
+ Field position in fields set
+
+ Raises
+ ------
+ : :class:`~pyasn1.error.PyAsn1Error`
+ If *name* is not present or not unique within callee *NamedTypes*
+ """
try:
- return self.__nameToPosIdx[name]
+ return self.__nameToPosMap[name]
+
except KeyError:
raise error.PyAsn1Error('Name %s not found' % (name,))
- def __buildAmbigiousTagMap(self):
- ambigiousTypes = ()
- idx = self.__namedTypesLen
- while idx > 0:
- idx = idx - 1
- t = self.__namedTypes[idx]
- if t.isOptional or t.isDefaulted:
- ambigiousTypes = (t, ) + ambigiousTypes
- else:
- ambigiousTypes = (t, )
- self.__ambigiousTypes[idx] = NamedTypes(*ambigiousTypes)
-
def getTagMapNearPosition(self, idx):
- if not self.__ambigiousTypes: self.__buildAmbigiousTagMap()
+ """Return ASN.1 types that are allowed at or past given field position.
+
+ Some ASN.1 serialisation allow for skipping optional and defaulted fields.
+ Some constructed ASN.1 types allow reordering of the fields. When recovering
+ such objects it may be important to know which types can possibly be
+ present at any given position in the field sets.
+
+ Parameters
+ ----------
+ idx: :py:class:`int`
+ Field index
+
+ Returns
+ -------
+ : :class:`~pyasn1.type.tagmap.TagMap`
+ Map if ASN.1 types allowed at given field position
+
+ Raises
+ ------
+ : :class:`~pyasn1.error.PyAsn1Error`
+ If given position is out of fields range
+ """
try:
- return self.__ambigiousTypes[idx].getTagMap()
+ return self.__ambiguousTypes[idx].tagMap
+
except KeyError:
raise error.PyAsn1Error('Type position out of range')
def getPositionNearType(self, tagSet, idx):
- if not self.__ambigiousTypes: self.__buildAmbigiousTagMap()
+ """Return the closest field position where given ASN.1 type is allowed.
+
+ Some ASN.1 serialisation allow for skipping optional and defaulted fields.
+ Some constructed ASN.1 types allow reordering of the fields. When recovering
+ such objects it may be important to know at which field position, in field set,
+ given *tagSet* is allowed at or past *idx* position.
+
+ Parameters
+ ----------
+ tagSet: :class:`~pyasn1.type.tag.TagSet`
+ ASN.1 type which field position to look up
+
+ idx: :py:class:`int`
+ Field position at or past which to perform ASN.1 type look up
+
+ Returns
+ -------
+ : :py:class:`int`
+ Field position in fields set
+
+ Raises
+ ------
+ : :class:`~pyasn1.error.PyAsn1Error`
+ If *tagSet* is not present or not unique within callee *NamedTypes*
+ or *idx* is out of fields range
+ """
try:
- return idx+self.__ambigiousTypes[idx].getPositionByType(tagSet)
+ return idx + self.__ambiguousTypes[idx].getPositionByType(tagSet)
+
except KeyError:
raise error.PyAsn1Error('Type position out of range')
- def genMinTagSet(self):
- if self.__minTagSet is None:
- for t in self.__namedTypes:
- __type = t.getType()
- tagSet = getattr(__type,'getMinTagSet',__type.getTagSet)()
- if self.__minTagSet is None or tagSet < self.__minTagSet:
- self.__minTagSet = tagSet
+ def __computeMinTagSet(self):
+ minTagSet = None
+ for namedType in self.__namedTypes:
+ asn1Object = namedType.asn1Object
+
+ try:
+ tagSet = asn1Object.minTagSet
+
+ except AttributeError:
+ tagSet = asn1Object.tagSet
+
+ if minTagSet is None or tagSet < minTagSet:
+ minTagSet = tagSet
+
+ return minTagSet or tag.TagSet()
+
+ @property
+ def minTagSet(self):
+ """Return the minimal TagSet among ASN.1 type in callee *NamedTypes*.
+
+ Some ASN.1 types/serialisation protocols require ASN.1 types to be
+ arranged based on their numerical tag value. The *minTagSet* property
+ returns that.
+
+ Returns
+ -------
+ : :class:`~pyasn1.type.tagset.TagSet`
+ Minimal TagSet among ASN.1 types in callee *NamedTypes*
+ """
return self.__minTagSet
-
- def getTagMap(self, uniq=False):
- if self.__tagMap[uniq] is None:
- tagMap = tagmap.TagMap()
- for nt in self.__namedTypes:
- tagMap = tagMap.clone(
- nt.getType(), nt.getType().getTagMap(), uniq
- )
- self.__tagMap[uniq] = tagMap
- return self.__tagMap[uniq]
+
+ def __computeTagMaps(self, unique):
+ presentTypes = {}
+ skipTypes = {}
+ defaultType = None
+ for namedType in self.__namedTypes:
+ tagMap = namedType.asn1Object.tagMap
+ if isinstance(tagMap, NamedTypes.PostponedError):
+ return tagMap
+ for tagSet in tagMap:
+ if unique and tagSet in presentTypes:
+ return NamedTypes.PostponedError('Non-unique tagSet %s of %s at %s' % (tagSet, namedType, self))
+ presentTypes[tagSet] = namedType.asn1Object
+ skipTypes.update(tagMap.skipTypes)
+
+ if defaultType is None:
+ defaultType = tagMap.defaultType
+ elif tagMap.defaultType is not None:
+ return NamedTypes.PostponedError('Duplicate default ASN.1 type at %s' % (self,))
+
+ return tagmap.TagMap(presentTypes, skipTypes, defaultType)
+
+ @property
+ def tagMap(self):
+ """Return a *TagMap* object from tags and types recursively.
+
+ Return a :class:`~pyasn1.type.tagmap.TagMap` object by
+ combining tags from *TagMap* objects of children types and
+ associating them with their immediate child type.
+
+ Example
+ -------
+ .. code-block:: python
+
+ OuterType ::= CHOICE {
+ innerType INTEGER
+ }
+
+ Calling *.tagMap* on *OuterType* will yield a map like this:
+
+ .. code-block:: python
+
+ Integer.tagSet -> Choice
+ """
+ return self.__nonUniqueTagMap
+
+ @property
+ def tagMapUnique(self):
+ """Return a *TagMap* object from unique tags and types recursively.
+
+ Return a :class:`~pyasn1.type.tagmap.TagMap` object by
+ combining tags from *TagMap* objects of children types and
+ associating them with their immediate child type.
+
+ Example
+ -------
+ .. code-block:: python
+
+ OuterType ::= CHOICE {
+ innerType INTEGER
+ }
+
+ Calling *.tagMapUnique* on *OuterType* will yield a map like this:
+
+ .. code-block:: python
+
+ Integer.tagSet -> Choice
+
+ Note
+ ----
+
+ Duplicate *TagSet* objects found in the tree of children
+ types would cause error.
+ """
+ return self.__uniqueTagMap
+
+ @property
+ def hasOptionalOrDefault(self):
+ return self.__hasOptionalOrDefault
+
+ @property
+ def hasOpenTypes(self):
+ return self.__hasOpenTypes
+
+ @property
+ def namedTypes(self):
+ return tuple(self.__namedTypes)
+
+ @property
+ def requiredComponents(self):
+ return self.__requiredComponents
diff -u -r 1.9.67/google_appengine/lib/pyasn1/pyasn1/type/namedval.py 1.9.68/google_appengine/lib/pyasn1/pyasn1/type/namedval.py
--- 1.9.67/google_appengine/lib/pyasn1/pyasn1/type/namedval.py 2018-03-09 20:05:30.000000000 -0800
+++ 1.9.68/google_appengine/lib/pyasn1/pyasn1/type/namedval.py 2018-04-03 21:35:14.000000000 -0700
@@ -1,58 +1,191 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2017, Ilya Etingof <[email protected]>
+# License: http://snmplabs.com/pyasn1/license.html
+#
# ASN.1 named integers
+#
from pyasn1 import error
-__all__ = [ 'NamedValues' ]
+__all__ = ['NamedValues']
+
+
+class NamedValues(object):
+ """Create named values object.
+
+ The |NamedValues| object represents a collection of string names
+ associated with numeric IDs. These objects are used for giving
+ names to otherwise numerical values.
+
+ |NamedValues| objects are immutable and duck-type Python
+ :class:`dict` object mapping ID to name and vice-versa.
+
+ Parameters
+ ----------
+ \*args: variable number of two-element :py:class:`tuple`
+
+ name: :py:class:`str`
+ Value label
+
+ value: :py:class:`int`
+ Numeric value
+
+ Keyword Args
+ ------------
+ name: :py:class:`str`
+ Value label
+
+ value: :py:class:`int`
+ Numeric value
+
+ Examples
+ --------
+
+ .. code-block:: pycon
+
+ >>> nv = NamedValues('a', 'b', ('c', 0), d=1)
+ >>> nv
+ >>> {'c': 0, 'd': 1, 'a': 2, 'b': 3}
+ >>> nv[0]
+ 'c'
+ >>> nv['a']
+ 2
+ """
+ def __init__(self, *args, **kwargs):
+ self.__names = {}
+ self.__numbers = {}
+
+ anonymousNames = []
+
+ for namedValue in args:
+ if isinstance(namedValue, (tuple, list)):
+ try:
+ name, number = namedValue
+
+ except ValueError:
+ raise error.PyAsn1Error('Not a proper attribute-value pair %r' % (namedValue,))
-class NamedValues:
- def __init__(self, *namedValues):
- self.nameToValIdx = {}; self.valToNameIdx = {}
- self.namedValues = ()
- automaticVal = 1
- for namedValue in namedValues:
- if isinstance(namedValue, tuple):
- name, val = namedValue
else:
- name = namedValue
- val = automaticVal
- if name in self.nameToValIdx:
+ anonymousNames.append(namedValue)
+ continue
+
+ if name in self.__names:
raise error.PyAsn1Error('Duplicate name %s' % (name,))
- self.nameToValIdx[name] = val
- if val in self.valToNameIdx:
- raise error.PyAsn1Error('Duplicate value %s=%s' % (name, val))
- self.valToNameIdx[val] = name
- self.namedValues = self.namedValues + ((name, val),)
- automaticVal = automaticVal + 1
+
+ if number in self.__numbers:
+ raise error.PyAsn1Error('Duplicate number %s=%s' % (name, number))
+
+ self.__names[name] = number
+ self.__numbers[number] = name
+
+ for name, number in kwargs.items():
+ if name in self.__names:
+ raise error.PyAsn1Error('Duplicate name %s' % (name,))
+
+ if number in self.__numbers:
+ raise error.PyAsn1Error('Duplicate number %s=%s' % (name, number))
+
+ self.__names[name] = number
+ self.__numbers[number] = name
+
+ if anonymousNames:
+
+ number = self.__numbers and max(self.__numbers) + 1 or 0
+
+ for name in anonymousNames:
+
+ if name in self.__names:
+ raise error.PyAsn1Error('Duplicate name %s' % (name,))
+
+ self.__names[name] = number
+ self.__numbers[number] = name
+
+ number += 1
def __repr__(self):
- return '%s(%s)' % (self.__class__.__name__, ', '.join([repr(x) for x in self.namedValues]))
+ representation = ', '.join(['%s=%d' % x for x in self.items()])
- def __str__(self): return str(self.namedValues)
+ if len(representation) > 64:
+ representation = representation[:32] + '...' + representation[-32:]
- def __eq__(self, other): return tuple(self) == tuple(other)
- def __ne__(self, other): return tuple(self) != tuple(other)
- def __lt__(self, other): return tuple(self) < tuple(other)
- def __le__(self, other): return tuple(self) <= tuple(other)
- def __gt__(self, other): return tuple(self) > tuple(other)
- def __ge__(self, other): return tuple(self) >= tuple(other)
- def __hash__(self): return hash(tuple(self))
-
- def getName(self, value):
- if value in self.valToNameIdx:
- return self.valToNameIdx[value]
+ return '<%s object 0x%x enums %s>' % (self.__class__.__name__, id(self), representation)
- def getValue(self, name):
- if name in self.nameToValIdx:
- return self.nameToValIdx[name]
-
- def __getitem__(self, i): return self.namedValues[i]
- def __len__(self): return len(self.namedValues)
+ def __eq__(self, other):
+ return dict(self) == other
+
+ def __ne__(self, other):
+ return dict(self) != other
+
+ def __lt__(self, other):
+ return dict(self) < other
+
+ def __le__(self, other):
+ return dict(self) <= other
+
+ def __gt__(self, other):
+ return dict(self) > other
+
+ def __ge__(self, other):
+ return dict(self) >= other
+
+ def __hash__(self):
+ return hash(self.items())
+
+ # Python dict protocol (read-only)
+
+ def __getitem__(self, key):
+ try:
+ return self.__numbers[key]
+
+ except KeyError:
+ return self.__names[key]
+
+ def __len__(self):
+ return len(self.__names)
+
+ def __contains__(self, key):
+ return key in self.__names or key in self.__numbers
+
+ def __iter__(self):
+ return iter(self.__names)
+
+ def values(self):
+ return iter(self.__numbers)
+
+ def keys(self):
+ return iter(self.__names)
+
+ def items(self):
+ for name in self.__names:
+ yield name, self.__names[name]
+
+ # support merging
def __add__(self, namedValues):
- return self.__class__(*self.namedValues + namedValues)
- def __radd__(self, namedValues):
- return self.__class__(*namedValues + tuple(self))
-
- def clone(self, *namedValues):
- return self.__class__(*tuple(self) + namedValues)
+ return self.__class__(*tuple(self.items()) + tuple(namedValues.items()))
+
+ # XXX clone/subtype?
+
+ def clone(self, *args, **kwargs):
+ new = self.__class__(*args, **kwargs)
+ return self + new
+
+ # legacy protocol
+
+ def getName(self, value):
+ if value in self.__numbers:
+ return self.__numbers[value]
+
+ def getValue(self, name):
+ if name in self.__names:
+ return self.__names[name]
-# XXX clone/subtype?
+ def getValues(self, *names):
+ try:
+ return [self.__names[name] for name in names]
+
+ except KeyError:
+ raise error.PyAsn1Error(
+ 'Unknown bit identifier(s): %s' % (set(names).difference(self.__names),)
+ )
Only in 1.9.68/google_appengine/lib/pyasn1/pyasn1/type: opentype.py
diff -u -r 1.9.67/google_appengine/lib/pyasn1/pyasn1/type/tag.py 1.9.68/google_appengine/lib/pyasn1/pyasn1/type/tag.py
--- 1.9.67/google_appengine/lib/pyasn1/pyasn1/type/tag.py 2018-03-09 20:05:30.000000000 -0800
+++ 1.9.68/google_appengine/lib/pyasn1/pyasn1/type/tag.py 2018-04-03 21:35:14.000000000 -0700
@@ -1,128 +1,333 @@
-# ASN.1 types tags
-from operator import getitem
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2017, Ilya Etingof <[email protected]>
+# License: http://snmplabs.com/pyasn1/license.html
+#
from pyasn1 import error
+__all__ = ['tagClassUniversal', 'tagClassApplication', 'tagClassContext',
+ 'tagClassPrivate', 'tagFormatSimple', 'tagFormatConstructed',
+ 'tagCategoryImplicit', 'tagCategoryExplicit',
+ 'tagCategoryUntagged', 'Tag', 'TagSet']
+
+#: Identifier for ASN.1 class UNIVERSAL
tagClassUniversal = 0x00
+
+#: Identifier for ASN.1 class APPLICATION
tagClassApplication = 0x40
+
+#: Identifier for ASN.1 class context-specific
tagClassContext = 0x80
+
+#: Identifier for ASN.1 class private
tagClassPrivate = 0xC0
+#: Identifier for "simple" ASN.1 structure (e.g. scalar)
tagFormatSimple = 0x00
+
+#: Identifier for "constructed" ASN.1 structure (e.g. may have inner components)
tagFormatConstructed = 0x20
tagCategoryImplicit = 0x01
tagCategoryExplicit = 0x02
tagCategoryUntagged = 0x04
-class Tag:
+
+class Tag(object):
+ """Create ASN.1 tag
+
+ Represents ASN.1 tag that can be attached to a ASN.1 type to make
+ types distinguishable from each other.
+
+ *Tag* objects are immutable and duck-type Python :class:`tuple` objects
+ holding three integer components of a tag.
+
+ Parameters
+ ----------
+ tagClass: :py:class:`int`
+ Tag *class* value
+
+ tagFormat: :py:class:`int`
+ Tag *format* value
+
+ tagId: :py:class:`int`
+ Tag ID value
+ """
def __init__(self, tagClass, tagFormat, tagId):
if tagId < 0:
- raise error.PyAsn1Error(
- 'Negative tag ID (%s) not allowed' % (tagId,)
- )
- self.__tag = (tagClass, tagFormat, tagId)
- self.uniq = (tagClass, tagId)
- self.__hashedUniqTag = hash(self.uniq)
-
- def __str__(self):
- return '[%s:%s:%s]' % self.__tag
+ raise error.PyAsn1Error('Negative tag ID (%s) not allowed' % tagId)
+ self.__tagClass = tagClass
+ self.__tagFormat = tagFormat
+ self.__tagId = tagId
+ self.__tagClassId = tagClass, tagId
+ self.__hash = hash(self.__tagClassId)
def __repr__(self):
- return '%s(tagClass=%s, tagFormat=%s, tagId=%s)' % (
- (self.__class__.__name__,) + self.__tag
- )
- # These is really a hotspot -- expose public "uniq" attribute to save on
- # function calls
- def __eq__(self, other): return self.uniq == other.uniq
- def __ne__(self, other): return self.uniq != other.uniq
- def __lt__(self, other): return self.uniq < other.uniq
- def __le__(self, other): return self.uniq <= other.uniq
- def __gt__(self, other): return self.uniq > other.uniq
- def __ge__(self, other): return self.uniq >= other.uniq
- def __hash__(self): return self.__hashedUniqTag
- def __getitem__(self, idx): return self.__tag[idx]
+ representation = '[%s:%s:%s]' % (self.__tagClass, self.__tagFormat, self.__tagId)
+ return '<%s object at 0x%x tag %s>' % (self.__class__.__name__, id(self), representation)
+
+ def __eq__(self, other):
+ return self.__tagClassId == other
+
+ def __ne__(self, other):
+ return self.__tagClassId != other
+
+ def __lt__(self, other):
+ return self.__tagClassId < other
+
+ def __le__(self, other):
+ return self.__tagClassId <= other
+
+ def __gt__(self, other):
+ return self.__tagClassId > other
+
+ def __ge__(self, other):
+ return self.__tagClassId >= other
+
+ def __hash__(self):
+ return self.__hash
+
+ def __getitem__(self, idx):
+ if idx == 0:
+ return self.__tagClass
+ elif idx == 1:
+ return self.__tagFormat
+ elif idx == 2:
+ return self.__tagId
+ else:
+ raise IndexError()
+
+ def __iter__(self):
+ yield self.__tagClass
+ yield self.__tagFormat
+ yield self.__tagId
+
def __and__(self, otherTag):
- (tagClass, tagFormat, tagId) = otherTag
- return self.__class__(
- self.__tag&tagClass, self.__tag&tagFormat, self.__tag&tagId
- )
+ return self.__class__(self.__tagClass & otherTag.tagClass,
+ self.__tagFormat & otherTag.tagFormat,
+ self.__tagId & otherTag.tagId)
+
def __or__(self, otherTag):
- (tagClass, tagFormat, tagId) = otherTag
- return self.__class__(
- self.__tag[0]|tagClass,
- self.__tag[1]|tagFormat,
- self.__tag[2]|tagId
+ return self.__class__(self.__tagClass | otherTag.tagClass,
+ self.__tagFormat | otherTag.tagFormat,
+ self.__tagId | otherTag.tagId)
+
+ @property
+ def tagClass(self):
+ """ASN.1 tag class
+
+ Returns
+ -------
+ : :py:class:`int`
+ Tag class
+ """
+ return self.__tagClass
+
+ @property
+ def tagFormat(self):
+ """ASN.1 tag format
+
+ Returns
+ -------
+ : :py:class:`int`
+ Tag format
+ """
+ return self.__tagFormat
+
+ @property
+ def tagId(self):
+ """ASN.1 tag ID
+
+ Returns
+ -------
+ : :py:class:`int`
+ Tag ID
+ """
+ return self.__tagId
+
+
+class TagSet(object):
+ """Create a collection of ASN.1 tags
+
+ Represents a combination of :class:`~pyasn1.type.tag.Tag` objects
+ that can be attached to a ASN.1 type to make types distinguishable
+ from each other.
+
+ *TagSet* objects are immutable and duck-type Python :class:`tuple` objects
+ holding arbitrary number of :class:`~pyasn1.type.tag.Tag` objects.
+
+ Parameters
+ ----------
+ baseTag: :class:`~pyasn1.type.tag.Tag`
+ Base *Tag* object. This tag survives IMPLICIT tagging.
+
+ *superTags: :class:`~pyasn1.type.tag.Tag`
+ Additional *Tag* objects taking part in subtyping.
+
+ Examples
+ --------
+ .. code-block:: python
+
+ class OrderNumber(NumericString):
+ '''
+ ASN.1 specification
+
+ Order-number ::=
+ [APPLICATION 5] IMPLICIT NumericString
+ '''
+ tagSet = NumericString.tagSet.tagImplicitly(
+ Tag(tagClassApplication, tagFormatSimple, 5)
)
- def asTuple(self): return self.__tag # __getitem__() is slow
-
-class TagSet:
+
+ orderNumber = OrderNumber('1234')
+ """
def __init__(self, baseTag=(), *superTags):
self.__baseTag = baseTag
self.__superTags = superTags
- self.__hashedSuperTags = hash(superTags)
- _uniq = ()
- for t in superTags:
- _uniq = _uniq + t.uniq
- self.uniq = _uniq
+ self.__superTagsClassId = tuple(
+ [(superTag.tagClass, superTag.tagId) for superTag in superTags]
+ )
self.__lenOfSuperTags = len(superTags)
+ self.__hash = hash(self.__superTagsClassId)
- def __str__(self):
- return self.__superTags and '+'.join([str(x) for x in self.__superTags]) or '[untagged]'
-
def __repr__(self):
- return '%s(%s)' % (
- self.__class__.__name__,
- '(), ' + ', '.join([repr(x) for x in self.__superTags])
- )
+ representation = '-'.join(['%s:%s:%s' % (x.tagClass, x.tagFormat, x.tagId)
+ for x in self.__superTags])
+ if representation:
+ representation = 'tags ' + representation
+ else:
+ representation = 'untagged'
+
+ return '<%s object at 0x%x %s>' % (self.__class__.__name__, id(self), representation)
def __add__(self, superTag):
- return self.__class__(
- self.__baseTag, *self.__superTags + (superTag,)
- )
+ return self.__class__(self.__baseTag, *self.__superTags + (superTag,))
+
def __radd__(self, superTag):
- return self.__class__(
- self.__baseTag, *(superTag,) + self.__superTags
- )
+ return self.__class__(self.__baseTag, *(superTag,) + self.__superTags)
+
+ def __getitem__(self, i):
+ if i.__class__ is slice:
+ return self.__class__(self.__baseTag, *self.__superTags[i])
+ else:
+ return self.__superTags[i]
+
+ def __eq__(self, other):
+ return self.__superTagsClassId == other
+
+ def __ne__(self, other):
+ return self.__superTagsClassId != other
+
+ def __lt__(self, other):
+ return self.__superTagsClassId < other
+
+ def __le__(self, other):
+ return self.__superTagsClassId <= other
+
+ def __gt__(self, other):
+ return self.__superTagsClassId > other
+
+ def __ge__(self, other):
+ return self.__superTagsClassId >= other
+
+ def __hash__(self):
+ return self.__hash
+
+ def __len__(self):
+ return self.__lenOfSuperTags
+
+ @property
+ def baseTag(self):
+ """Return base ASN.1 tag
+
+ Returns
+ -------
+ : :class:`~pyasn1.type.tag.Tag`
+ Base tag of this *TagSet*
+ """
+ return self.__baseTag
+
+ @property
+ def superTags(self):
+ """Return ASN.1 tags
+
+ Returns
+ -------
+ : :py:class:`tuple`
+ Tuple of :class:`~pyasn1.type.tag.Tag` objects that this *TagSet* contains
+ """
+ return self.__superTags
def tagExplicitly(self, superTag):
- tagClass, tagFormat, tagId = superTag
- if tagClass == tagClassUniversal:
- raise error.PyAsn1Error(
- 'Can\'t tag with UNIVERSAL-class tag'
- )
- if tagFormat != tagFormatConstructed:
- superTag = Tag(tagClass, tagFormatConstructed, tagId)
+ """Return explicitly tagged *TagSet*
+
+ Create a new *TagSet* representing callee *TagSet* explicitly tagged
+ with passed tag(s). With explicit tagging mode, new tags are appended
+ to existing tag(s).
+
+ Parameters
+ ----------
+ superTag: :class:`~pyasn1.type.tag.Tag`
+ *Tag* object to tag this *TagSet*
+
+ Returns
+ -------
+ : :class:`~pyasn1.type.tag.TagSet`
+ New *TagSet* object
+ """
+ if superTag.tagClass == tagClassUniversal:
+ raise error.PyAsn1Error("Can't tag with UNIVERSAL class tag")
+ if superTag.tagFormat != tagFormatConstructed:
+ superTag = Tag(superTag.tagClass, tagFormatConstructed, superTag.tagId)
return self + superTag
def tagImplicitly(self, superTag):
- tagClass, tagFormat, tagId = superTag
+ """Return implicitly tagged *TagSet*
+
+ Create a new *TagSet* representing callee *TagSet* implicitly tagged
+ with passed tag(s). With implicit tagging mode, new tag(s) replace the
+ last existing tag.
+
+ Parameters
+ ----------
+ superTag: :class:`~pyasn1.type.tag.Tag`
+ *Tag* object to tag this *TagSet*
+
+ Returns
+ -------
+ : :class:`~pyasn1.type.tag.TagSet`
+ New *TagSet* object
+ """
if self.__superTags:
- superTag = Tag(tagClass, self.__superTags[-1][1], tagId)
+ superTag = Tag(superTag.tagClass, self.__superTags[-1].tagFormat, superTag.tagId)
return self[:-1] + superTag
- def getBaseTag(self): return self.__baseTag
- def __getitem__(self, idx):
- if isinstance(idx, slice):
- return self.__class__(
- self.__baseTag, *getitem(self.__superTags, idx)
- )
- return self.__superTags[idx]
- def __eq__(self, other): return self.uniq == other.uniq
- def __ne__(self, other): return self.uniq != other.uniq
- def __lt__(self, other): return self.uniq < other.uniq
- def __le__(self, other): return self.uniq <= other.uniq
- def __gt__(self, other): return self.uniq > other.uniq
- def __ge__(self, other): return self.uniq >= other.uniq
- def __hash__(self): return self.__hashedSuperTags
- def __len__(self): return self.__lenOfSuperTags
def isSuperTagSetOf(self, tagSet):
+ """Test type relationship against given *TagSet*
+
+ The callee is considered to be a supertype of given *TagSet*
+ tag-wise if all tags in *TagSet* are present in the callee and
+ they are in the same order.
+
+ Parameters
+ ----------
+ tagSet: :class:`~pyasn1.type.tag.TagSet`
+ *TagSet* object to evaluate against the callee
+
+ Returns
+ -------
+ : :py:class:`bool`
+ `True` if callee is a supertype of *tagSet*
+ """
if len(tagSet) < self.__lenOfSuperTags:
- return
- idx = self.__lenOfSuperTags - 1
- while idx >= 0:
- if self.__superTags[idx] != tagSet[idx]:
- return
- idx = idx - 1
- return 1
-
-def initTagSet(tag): return TagSet(tag, tag)
+ return False
+ return self.__superTags == tagSet[:self.__lenOfSuperTags]
+
+ # Backward compatibility
+
+ def getBaseTag(self):
+ return self.__baseTag
+
+def initTagSet(tag):
+ return TagSet(tag, tag)
diff -u -r 1.9.67/google_appengine/lib/pyasn1/pyasn1/type/tagmap.py 1.9.68/google_appengine/lib/pyasn1/pyasn1/type/tagmap.py
--- 1.9.67/google_appengine/lib/pyasn1/pyasn1/type/tagmap.py 2018-03-09 20:05:30.000000000 -0800
+++ 1.9.68/google_appengine/lib/pyasn1/pyasn1/type/tagmap.py 2018-04-03 21:35:14.000000000 -0700
@@ -1,66 +1,96 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2017, Ilya Etingof <[email protected]>
+# License: http://snmplabs.com/pyasn1/license.html
+#
from pyasn1 import error
-class TagMap:
- def __init__(self, posMap={}, negMap={}, defType=None):
- self.__posMap = posMap.copy()
- self.__negMap = negMap.copy()
- self.__defType = defType
-
+__all__ = ['TagMap']
+
+
+class TagMap(object):
+ """Map *TagSet* objects to ASN.1 types
+
+ Create an object mapping *TagSet* object to ASN.1 type.
+
+ *TagMap* objects are immutable and duck-type read-only Python
+ :class:`dict` objects holding *TagSet* objects as keys and ASN.1
+ type objects as values.
+
+ Parameters
+ ----------
+ presentTypes: :py:class:`dict`
+ Map of :class:`~pyasn1.type.tag.TagSet` to ASN.1 objects considered
+ as being unconditionally present in the *TagMap*.
+
+ skipTypes: :py:class:`dict`
+ A collection of :class:`~pyasn1.type.tag.TagSet` objects considered
+ as absent in the *TagMap* even when *defaultType* is present.
+
+ defaultType: ASN.1 type object
+ An ASN.1 type object callee *TagMap* returns for any *TagSet* key not present
+ in *presentTypes* (unless given key is present in *skipTypes*).
+ """
+ def __init__(self, presentTypes=None, skipTypes=None, defaultType=None):
+ self.__presentTypes = presentTypes or {}
+ self.__skipTypes = skipTypes or {}
+ self.__defaultType = defaultType
+
def __contains__(self, tagSet):
- return tagSet in self.__posMap or \
- self.__defType is not None and tagSet not in self.__negMap
+ return (tagSet in self.__presentTypes or
+ self.__defaultType is not None and tagSet not in self.__skipTypes)
def __getitem__(self, tagSet):
- if tagSet in self.__posMap:
- return self.__posMap[tagSet]
- elif tagSet in self.__negMap:
- raise error.PyAsn1Error('Key in negative map')
- elif self.__defType is not None:
- return self.__defType
- else:
- raise KeyError()
+ try:
+ return self.__presentTypes[tagSet]
+ except KeyError:
+ if self.__defaultType is None:
+ raise KeyError()
+ elif tagSet in self.__skipTypes:
+ raise error.PyAsn1Error('Key in negative map')
+ else:
+ return self.__defaultType
+
+ def __iter__(self):
+ return iter(self.__presentTypes)
def __repr__(self):
- s = self.__class__.__name__ + '('
- if self.__posMap:
- s = s + 'posMap=%r, ' % (self.__posMap,)
- if self.__negMap:
- s = s + 'negMap=%r, ' % (self.__negMap,)
- if self.__defType is not None:
- s = s + 'defType=%r' % (self.__defType,)
- return s + ')'
-
- def __str__(self):
- s = self.__class__.__name__ + ':\n'
- if self.__posMap:
- s = s + 'posMap:\n%s, ' % ',\n '.join([ x.prettyPrintType() for x in self.__posMap.values()])
- if self.__negMap:
- s = s + 'negMap:\n%s, ' % ',\n '.join([ x.prettyPrintType() for x in self.__negMap.values()])
- if self.__defType is not None:
- s = s + 'defType:\n%s, ' % self.__defType.prettyPrintType()
- return s
-
- def clone(self, parentType, tagMap, uniq=False):
- if self.__defType is not None and tagMap.getDef() is not None:
- raise error.PyAsn1Error('Duplicate default value at %s' % (self,))
- if tagMap.getDef() is not None:
- defType = tagMap.getDef()
- else:
- defType = self.__defType
-
- posMap = self.__posMap.copy()
- for k in tagMap.getPosMap():
- if uniq and k in posMap:
- raise error.PyAsn1Error('Duplicate positive key %s' % (k,))
- posMap[k] = parentType
-
- negMap = self.__negMap.copy()
- negMap.update(tagMap.getNegMap())
-
- return self.__class__(
- posMap, negMap, defType,
- )
-
- def getPosMap(self): return self.__posMap.copy()
- def getNegMap(self): return self.__negMap.copy()
- def getDef(self): return self.__defType
+ representation = '%s object at 0x%x' % (self.__class__.__name__, id(self))
+
+ if self.__presentTypes:
+ representation += ' present %s' % repr(self.__presentTypes)
+
+ if self.__skipTypes:
+ representation += ' skip %s' % repr(self.__skipTypes)
+
+ if self.__defaultType is not None:
+ representation += ' default %s' % repr(self.__defaultType)
+
+ return '<%s>' % representation
+
+ @property
+ def presentTypes(self):
+ """Return *TagSet* to ASN.1 type map present in callee *TagMap*"""
+ return self.__presentTypes
+
+ @property
+ def skipTypes(self):
+ """Return *TagSet* collection unconditionally absent in callee *TagMap*"""
+ return self.__skipTypes
+
+ @property
+ def defaultType(self):
+ """Return default ASN.1 type being returned for any missing *TagSet*"""
+ return self.__defaultType
+
+ # Backward compatibility
+
+ def getPosMap(self):
+ return self.presentTypes
+
+ def getNegMap(self):
+ return self.skipTypes
+
+ def getDef(self):
+ return self.defaultType
diff -u -r 1.9.67/google_appengine/lib/pyasn1/pyasn1/type/univ.py 1.9.68/google_appengine/lib/pyasn1/pyasn1/type/univ.py
--- 1.9.67/google_appengine/lib/pyasn1/pyasn1/type/univ.py 2018-03-09 20:05:30.000000000 -0800
+++ 1.9.68/google_appengine/lib/pyasn1/pyasn1/type/univ.py 2018-04-03 21:35:14.000000000 -0700
@@ -1,488 +1,1078 @@
-# ASN.1 "universal" data types
-import operator, sys, math
-from pyasn1.type import base, tag, constraint, namedtype, namedval, tagmap
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2017, Ilya Etingof <[email protected]>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import math
+import sys
+
+from pyasn1 import error
from pyasn1.codec.ber import eoo
+from pyasn1.compat import binary
+from pyasn1.compat import integer
from pyasn1.compat import octets
-from pyasn1 import error
+from pyasn1.type import base
+from pyasn1.type import constraint
+from pyasn1.type import namedtype
+from pyasn1.type import namedval
+from pyasn1.type import tag
+from pyasn1.type import tagmap
+
+NoValue = base.NoValue
+noValue = NoValue()
+
+__all__ = ['Integer', 'Boolean', 'BitString', 'OctetString', 'Null',
+ 'ObjectIdentifier', 'Real', 'Enumerated',
+ 'SequenceOfAndSetOfBase', 'SequenceOf', 'SetOf',
+ 'SequenceAndSetBase', 'Sequence', 'Set', 'Choice', 'Any',
+ 'NoValue', 'noValue']
# "Simple" ASN.1 types (yet incomplete)
+
class Integer(base.AbstractSimpleAsn1Item):
- tagSet = baseTagSet = tag.initTagSet(
+ """Create |ASN.1| type or object.
+
+ |ASN.1| objects are immutable and duck-type Python :class:`int` objects.
+
+ Keyword Args
+ ------------
+ value: :class:`int`, :class:`str` or |ASN.1| object
+ Python integer or string literal or |ASN.1| class instance.
+
+ tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+ Object representing non-default ASN.1 tag(s)
+
+ subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+ Object representing non-default ASN.1 subtype constraint(s)
+
+ namedValues: :py:class:`~pyasn1.type.namedval.NamedValues`
+ Object representing non-default symbolic aliases for numbers
+
+ Raises
+ ------
+ :py:class:`~pyasn1.error.PyAsn1Error`
+ On constraint violation or bad initializer.
+
+ Examples
+ --------
+
+ .. code-block:: python
+
+ class ErrorCode(Integer):
+ '''
+ ASN.1 specification:
+
+ ErrorCode ::=
+ INTEGER { disk-full(1), no-disk(-1),
+ disk-not-formatted(2) }
+
+ error ErrorCode ::= disk-full
+ '''
+ namedValues = NamedValues(
+ ('disk-full', 1), ('no-disk', -1),
+ ('disk-not-formatted', 2)
+ )
+
+ error = ErrorCode('disk-full')
+ """
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+ #: associated with |ASN.1| type.
+ tagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x02)
- )
+ )
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+ #: imposing constraints on |ASN.1| type initialization values.
+ subtypeSpec = constraint.ConstraintsIntersection()
+
+ #: Default :py:class:`~pyasn1.type.namedval.NamedValues` object
+ #: representing symbolic aliases for numbers
namedValues = namedval.NamedValues()
- def __init__(self, value=None, tagSet=None, subtypeSpec=None,
- namedValues=None):
- if namedValues is None:
- self.__namedValues = self.namedValues
- else:
- self.__namedValues = namedValues
- base.AbstractSimpleAsn1Item.__init__(
- self, value, tagSet, subtypeSpec
- )
- def __repr__(self):
- if self.__namedValues is not self.namedValues:
- return '%s, %r)' % (base.AbstractSimpleAsn1Item.__repr__(self)[:-1], self.__namedValues)
- else:
- return base.AbstractSimpleAsn1Item.__repr__(self)
-
- def __and__(self, value): return self.clone(self._value & value)
- def __rand__(self, value): return self.clone(value & self._value)
- def __or__(self, value): return self.clone(self._value | value)
- def __ror__(self, value): return self.clone(value | self._value)
- def __xor__(self, value): return self.clone(self._value ^ value)
- def __rxor__(self, value): return self.clone(value ^ self._value)
- def __lshift__(self, value): return self.clone(self._value << value)
- def __rshift__(self, value): return self.clone(self._value >> value)
-
- def __add__(self, value): return self.clone(self._value + value)
- def __radd__(self, value): return self.clone(value + self._value)
- def __sub__(self, value): return self.clone(self._value - value)
- def __rsub__(self, value): return self.clone(value - self._value)
- def __mul__(self, value): return self.clone(self._value * value)
- def __rmul__(self, value): return self.clone(value * self._value)
- def __mod__(self, value): return self.clone(self._value % value)
- def __rmod__(self, value): return self.clone(value % self._value)
- def __pow__(self, value, modulo=None): return self.clone(pow(self._value, value, modulo))
- def __rpow__(self, value): return self.clone(pow(value, self._value))
+ # Optimization for faster codec lookup
+ typeId = base.AbstractSimpleAsn1Item.getTypeId()
+
+ def __init__(self, value=noValue, **kwargs):
+ if 'namedValues' not in kwargs:
+ kwargs['namedValues'] = self.namedValues
+
+ base.AbstractSimpleAsn1Item.__init__(self, value, **kwargs)
+
+ def __and__(self, value):
+ return self.clone(self._value & value)
+
+ def __rand__(self, value):
+ return self.clone(value & self._value)
+
+ def __or__(self, value):
+ return self.clone(self._value | value)
+
+ def __ror__(self, value):
+ return self.clone(value | self._value)
+
+ def __xor__(self, value):
+ return self.clone(self._value ^ value)
+
+ def __rxor__(self, value):
+ return self.clone(value ^ self._value)
+
+ def __lshift__(self, value):
+ return self.clone(self._value << value)
+
+ def __rshift__(self, value):
+ return self.clone(self._value >> value)
+
+ def __add__(self, value):
+ return self.clone(self._value + value)
+
+ def __radd__(self, value):
+ return self.clone(value + self._value)
+
+ def __sub__(self, value):
+ return self.clone(self._value - value)
+
+ def __rsub__(self, value):
+ return self.clone(value - self._value)
+
+ def __mul__(self, value):
+ return self.clone(self._value * value)
+
+ def __rmul__(self, value):
+ return self.clone(value * self._value)
+
+ def __mod__(self, value):
+ return self.clone(self._value % value)
+
+ def __rmod__(self, value):
+ return self.clone(value % self._value)
+
+ def __pow__(self, value, modulo=None):
+ return self.clone(pow(self._value, value, modulo))
+
+ def __rpow__(self, value):
+ return self.clone(pow(value, self._value))
+
+ def __floordiv__(self, value):
+ return self.clone(self._value // value)
+
+ def __rfloordiv__(self, value):
+ return self.clone(value // self._value)
if sys.version_info[0] <= 2:
- def __div__(self, value): return self.clone(self._value // value)
- def __rdiv__(self, value): return self.clone(value // self._value)
+ def __div__(self, value):
+ if isinstance(value, float):
+ return Real(self._value / value)
+ else:
+ return self.clone(self._value / value)
+
+ def __rdiv__(self, value):
+ if isinstance(value, float):
+ return Real(value / self._value)
+ else:
+ return self.clone(value / self._value)
else:
- def __truediv__(self, value): return self.clone(self._value / value)
- def __rtruediv__(self, value): return self.clone(value / self._value)
- def __divmod__(self, value): return self.clone(self._value // value)
- def __rdivmod__(self, value): return self.clone(value // self._value)
+ def __truediv__(self, value):
+ return Real(self._value / value)
+
+ def __rtruediv__(self, value):
+ return Real(value / self._value)
+
+ def __divmod__(self, value):
+ return self.clone(divmod(self._value, value))
+
+ def __rdivmod__(self, value):
+ return self.clone(divmod(value, self._value))
__hash__ = base.AbstractSimpleAsn1Item.__hash__
- def __int__(self): return int(self._value)
+ def __int__(self):
+ return int(self._value)
+
if sys.version_info[0] <= 2:
- def __long__(self): return long(self._value)
- def __float__(self): return float(self._value)
- def __abs__(self): return self.clone(abs(self._value))
- def __index__(self): return int(self._value)
- def __pos__(self): return self.clone(+self._value)
- def __neg__(self): return self.clone(-self._value)
- def __invert__(self): return self.clone(~self._value)
+ def __long__(self):
+ return long(self._value)
+
+ def __float__(self):
+ return float(self._value)
+
+ def __abs__(self):
+ return self.clone(abs(self._value))
+
+ def __index__(self):
+ return int(self._value)
+
+ def __pos__(self):
+ return self.clone(+self._value)
+
+ def __neg__(self):
+ return self.clone(-self._value)
+
+ def __invert__(self):
+ return self.clone(~self._value)
+
def __round__(self, n=0):
r = round(self._value, n)
if n:
return self.clone(r)
else:
return r
- def __floor__(self): return math.floor(self._value)
- def __ceil__(self): return math.ceil(self._value)
+
+ def __floor__(self):
+ return math.floor(self._value)
+
+ def __ceil__(self):
+ return math.ceil(self._value)
+
if sys.version_info[0:2] > (2, 5):
- def __trunc__(self): return self.clone(math.trunc(self._value))
+ def __trunc__(self):
+ return self.clone(math.trunc(self._value))
+
+ def __lt__(self, value):
+ return self._value < value
+
+ def __le__(self, value):
+ return self._value <= value
- def __lt__(self, value): return self._value < value
- def __le__(self, value): return self._value <= value
- def __eq__(self, value): return self._value == value
- def __ne__(self, value): return self._value != value
- def __gt__(self, value): return self._value > value
- def __ge__(self, value): return self._value >= value
+ def __eq__(self, value):
+ return self._value == value
+
+ def __ne__(self, value):
+ return self._value != value
+
+ def __gt__(self, value):
+ return self._value > value
+
+ def __ge__(self, value):
+ return self._value >= value
def prettyIn(self, value):
- if not isinstance(value, str):
+ try:
+ return int(value)
+
+ except ValueError:
try:
- return int(value)
- except:
+ return self.namedValues[value]
+
+ except KeyError:
raise error.PyAsn1Error(
'Can\'t coerce %r into integer: %s' % (value, sys.exc_info()[1])
- )
- r = self.__namedValues.getValue(value)
- if r is not None:
- return r
- try:
- return int(value)
- except:
- raise error.PyAsn1Error(
- 'Can\'t coerce %r into integer: %s' % (value, sys.exc_info()[1])
)
def prettyOut(self, value):
- r = self.__namedValues.getName(value)
- return r is None and str(value) or repr(r)
+ try:
+ return str(self.namedValues[value])
- def getNamedValues(self): return self.__namedValues
+ except KeyError:
+ return str(value)
+
+ # backward compatibility
+
+ def getNamedValues(self):
+ return self.namedValues
- def clone(self, value=None, tagSet=None, subtypeSpec=None,
- namedValues=None):
- if value is None and tagSet is None and subtypeSpec is None \
- and namedValues is None:
- return self
- if value is None:
- value = self._value
- if tagSet is None:
- tagSet = self._tagSet
- if subtypeSpec is None:
- subtypeSpec = self._subtypeSpec
- if namedValues is None:
- namedValues = self.__namedValues
- return self.__class__(value, tagSet, subtypeSpec, namedValues)
-
- def subtype(self, value=None, implicitTag=None, explicitTag=None,
- subtypeSpec=None, namedValues=None):
- if value is None:
- value = self._value
- if implicitTag is not None:
- tagSet = self._tagSet.tagImplicitly(implicitTag)
- elif explicitTag is not None:
- tagSet = self._tagSet.tagExplicitly(explicitTag)
- else:
- tagSet = self._tagSet
- if subtypeSpec is None:
- subtypeSpec = self._subtypeSpec
- else:
- subtypeSpec = subtypeSpec + self._subtypeSpec
- if namedValues is None:
- namedValues = self.__namedValues
- else:
- namedValues = namedValues + self.__namedValues
- return self.__class__(value, tagSet, subtypeSpec, namedValues)
class Boolean(Integer):
- tagSet = baseTagSet = tag.initTagSet(
+ """Create |ASN.1| type or object.
+
+ |ASN.1| objects are immutable and duck-type Python :class:`int` objects.
+
+ Keyword Args
+ ------------
+ value: :class:`int`, :class:`str` or |ASN.1| object
+ Python integer or boolean or string literal or |ASN.1| class instance.
+
+ tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+ Object representing non-default ASN.1 tag(s)
+
+ subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+ Object representing non-default ASN.1 subtype constraint(s)
+
+ namedValues: :py:class:`~pyasn1.type.namedval.NamedValues`
+ Object representing non-default symbolic aliases for numbers
+
+ Raises
+ ------
+ :py:class:`~pyasn1.error.PyAsn1Error`
+ On constraint violation or bad initializer.
+
+ Examples
+ --------
+ .. code-block:: python
+
+ class RoundResult(Boolean):
+ '''
+ ASN.1 specification:
+
+ RoundResult ::= BOOLEAN
+
+ ok RoundResult ::= TRUE
+ ko RoundResult ::= FALSE
+ '''
+ ok = RoundResult(True)
+ ko = RoundResult(False)
+ """
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+ #: associated with |ASN.1| type.
+ tagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x01),
- )
- subtypeSpec = Integer.subtypeSpec+constraint.SingleValueConstraint(0,1)
- namedValues = Integer.namedValues.clone(('False', 0), ('True', 1))
+ )
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+ #: imposing constraints on |ASN.1| type initialization values.
+ subtypeSpec = Integer.subtypeSpec + constraint.SingleValueConstraint(0, 1)
+
+ #: Default :py:class:`~pyasn1.type.namedval.NamedValues` object
+ #: representing symbolic aliases for numbers
+ namedValues = namedval.NamedValues(('False', 0), ('True', 1))
+
+ # Optimization for faster codec lookup
+ typeId = Integer.getTypeId()
+
+if sys.version_info[0] < 3:
+ SizedIntegerBase = long
+else:
+ SizedIntegerBase = int
+
+
+class SizedInteger(SizedIntegerBase):
+ bitLength = leadingZeroBits = None
+
+ def setBitLength(self, bitLength):
+ self.bitLength = bitLength
+ self.leadingZeroBits = max(bitLength - integer.bitLength(self), 0)
+ return self
+
+ def __len__(self):
+ if self.bitLength is None:
+ self.setBitLength(integer.bitLength(self))
+
+ return self.bitLength
+
class BitString(base.AbstractSimpleAsn1Item):
- tagSet = baseTagSet = tag.initTagSet(
+ """Create |ASN.1| schema or value object.
+
+ |ASN.1| objects are immutable and duck-type both Python :class:`tuple` (as a tuple
+ of bits) and :class:`int` objects.
+
+ Keyword Args
+ ------------
+ value: :class:`int`, :class:`str` or |ASN.1| object
+ Python integer or string literal representing binary or hexadecimal
+ number or sequence of integer bits or |ASN.1| object.
+
+ tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+ Object representing non-default ASN.1 tag(s)
+
+ subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+ Object representing non-default ASN.1 subtype constraint(s)
+
+ namedValues: :py:class:`~pyasn1.type.namedval.NamedValues`
+ Object representing non-default symbolic aliases for numbers
+
+ binValue: :py:class:`str`
+ Binary string initializer to use instead of the *value*.
+ Example: '10110011'.
+
+ hexValue: :py:class:`str`
+ Hexadecimal string initializer to use instead of the *value*.
+ Example: 'DEADBEEF'.
+
+ Raises
+ ------
+ :py:class:`~pyasn1.error.PyAsn1Error`
+ On constraint violation or bad initializer.
+
+ Examples
+ --------
+ .. code-block:: python
+
+ class Rights(BitString):
+ '''
+ ASN.1 specification:
+
+ Rights ::= BIT STRING { user-read(0), user-write(1),
+ group-read(2), group-write(3),
+ other-read(4), other-write(5) }
+
+ group1 Rights ::= { group-read, group-write }
+ group2 Rights ::= '0011'B
+ group3 Rights ::= '3'H
+ '''
+ namedValues = NamedValues(
+ ('user-read', 0), ('user-write', 1),
+ ('group-read', 2), ('group-write', 3),
+ ('other-read', 4), ('other-write', 5)
+ )
+
+ group1 = Rights(('group-read', 'group-write'))
+ group2 = Rights('0011')
+ group3 = Rights(0x3)
+ """
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+ #: associated with |ASN.1| type.
+ tagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x03)
- )
+ )
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+ #: imposing constraints on |ASN.1| type initialization values.
+ subtypeSpec = constraint.ConstraintsIntersection()
+
+ #: Default :py:class:`~pyasn1.type.namedval.NamedValues` object
+ #: representing symbolic aliases for numbers
namedValues = namedval.NamedValues()
- def __init__(self, value=None, tagSet=None, subtypeSpec=None,
- namedValues=None):
- if namedValues is None:
- self.__namedValues = self.namedValues
- else:
- self.__namedValues = namedValues
- base.AbstractSimpleAsn1Item.__init__(
- self, value, tagSet, subtypeSpec
- )
- def clone(self, value=None, tagSet=None, subtypeSpec=None,
- namedValues=None):
- if value is None and tagSet is None and subtypeSpec is None \
- and namedValues is None:
- return self
- if value is None:
- value = self._value
- if tagSet is None:
- tagSet = self._tagSet
- if subtypeSpec is None:
- subtypeSpec = self._subtypeSpec
- if namedValues is None:
- namedValues = self.__namedValues
- return self.__class__(value, tagSet, subtypeSpec, namedValues)
-
- def subtype(self, value=None, implicitTag=None, explicitTag=None,
- subtypeSpec=None, namedValues=None):
- if value is None:
- value = self._value
- if implicitTag is not None:
- tagSet = self._tagSet.tagImplicitly(implicitTag)
- elif explicitTag is not None:
- tagSet = self._tagSet.tagExplicitly(explicitTag)
- else:
- tagSet = self._tagSet
- if subtypeSpec is None:
- subtypeSpec = self._subtypeSpec
- else:
- subtypeSpec = subtypeSpec + self._subtypeSpec
- if namedValues is None:
- namedValues = self.__namedValues
- else:
- namedValues = namedValues + self.__namedValues
- return self.__class__(value, tagSet, subtypeSpec, namedValues)
+ # Optimization for faster codec lookup
+ typeId = base.AbstractSimpleAsn1Item.getTypeId()
+
+ defaultBinValue = defaultHexValue = noValue
+
+ def __init__(self, value=noValue, **kwargs):
+ if value is noValue:
+ if kwargs:
+ try:
+ value = self.fromBinaryString(kwargs.pop('binValue'), internalFormat=True)
+
+ except KeyError:
+ pass
+
+ try:
+ value = self.fromHexString(kwargs.pop('hexValue'), internalFormat=True)
+
+ except KeyError:
+ pass
+
+ if value is noValue:
+ if self.defaultBinValue is not noValue:
+ value = self.fromBinaryString(self.defaultBinValue, internalFormat=True)
+
+ elif self.defaultHexValue is not noValue:
+ value = self.fromHexString(self.defaultHexValue, internalFormat=True)
+
+ if 'namedValues' not in kwargs:
+ kwargs['namedValues'] = self.namedValues
+
+ base.AbstractSimpleAsn1Item.__init__(self, value, **kwargs)
+
+ def __str__(self):
+ return self.asBinary()
+
+ def __eq__(self, other):
+ other = self.prettyIn(other)
+ return self is other or self._value == other and len(self._value) == len(other)
- def __str__(self): return str(tuple(self))
+ def __ne__(self, other):
+ other = self.prettyIn(other)
+ return self._value != other or len(self._value) != len(other)
+
+ def __lt__(self, other):
+ other = self.prettyIn(other)
+ return len(self._value) < len(other) or len(self._value) == len(other) and self._value < other
+
+ def __le__(self, other):
+ other = self.prettyIn(other)
+ return len(self._value) <= len(other) or len(self._value) == len(other) and self._value <= other
+
+ def __gt__(self, other):
+ other = self.prettyIn(other)
+ return len(self._value) > len(other) or len(self._value) == len(other) and self._value > other
+
+ def __ge__(self, other):
+ other = self.prettyIn(other)
+ return len(self._value) >= len(other) or len(self._value) == len(other) and self._value >= other
# Immutable sequence object protocol
def __len__(self):
- if self._len is None:
- self._len = len(self._value)
- return self._len
+ return len(self._value)
+
def __getitem__(self, i):
- if isinstance(i, slice):
- return self.clone(operator.getitem(self._value, i))
+ if i.__class__ is slice:
+ return self.clone([self[x] for x in range(*i.indices(len(self)))])
else:
- return self._value[i]
+ length = len(self._value) - 1
+ if i > length or i < 0:
+ raise IndexError('bit index out of range')
+ return (self._value >> (length - i)) & 1
+
+ def __iter__(self):
+ length = len(self._value)
+ while length:
+ length -= 1
+ yield (self._value >> length) & 1
+
+ def __reversed__(self):
+ return reversed(tuple(self))
+
+ # arithmetic operators
+
+ def __add__(self, value):
+ value = self.prettyIn(value)
+ return self.clone(SizedInteger(self._value << len(value) | value).setBitLength(len(self._value) + len(value)))
+
+ def __radd__(self, value):
+ value = self.prettyIn(value)
+ return self.clone(SizedInteger(value << len(self._value) | self._value).setBitLength(len(self._value) + len(value)))
+
+ def __mul__(self, value):
+ bitString = self._value
+ while value > 1:
+ bitString <<= len(self._value)
+ bitString |= self._value
+ value -= 1
+ return self.clone(bitString)
+
+ def __rmul__(self, value):
+ return self * value
+
+ def __lshift__(self, count):
+ return self.clone(SizedInteger(self._value << count).setBitLength(len(self._value) + count))
+
+ def __rshift__(self, count):
+ return self.clone(SizedInteger(self._value >> count).setBitLength(max(0, len(self._value) - count)))
+
+ def __int__(self):
+ return self._value
+
+ def __float__(self):
+ return float(self._value)
+
+ if sys.version_info[0] < 3:
+ def __long__(self):
+ return self._value
+
+ def asNumbers(self):
+ """Get |ASN.1| value as a sequence of 8-bit integers.
+
+ If |ASN.1| object length is not a multiple of 8, result
+ will be left-padded with zeros.
+ """
+ return tuple(octets.octs2ints(self.asOctets()))
+
+ def asOctets(self):
+ """Get |ASN.1| value as a sequence of octets.
+
+ If |ASN.1| object length is not a multiple of 8, result
+ will be left-padded with zeros.
+ """
+ return integer.to_bytes(self._value, length=len(self))
+
+ def asInteger(self):
+ """Get |ASN.1| value as a single integer value.
+ """
+ return self._value
+
+ def asBinary(self):
+ """Get |ASN.1| value as a text string of bits.
+ """
+ binString = binary.bin(self._value)[2:]
+ return '0' * (len(self._value) - len(binString)) + binString
+
+ @classmethod
+ def fromHexString(cls, value, internalFormat=False, prepend=None):
+ """Create a |ASN.1| object initialized from the hex string.
+
+ Parameters
+ ----------
+ value: :class:`str`
+ Text string like 'DEADBEEF'
+ """
+ try:
+ value = SizedInteger(value, 16).setBitLength(len(value) * 4)
+
+ except ValueError:
+ raise error.PyAsn1Error('%s.fromHexString() error: %s' % (cls.__name__, sys.exc_info()[1]))
+
+ if prepend is not None:
+ value = SizedInteger(
+ (SizedInteger(prepend) << len(value)) | value
+ ).setBitLength(len(prepend) + len(value))
+
+ if not internalFormat:
+ value = cls(value)
+
+ return value
+
+ @classmethod
+ def fromBinaryString(cls, value, internalFormat=False, prepend=None):
+ """Create a |ASN.1| object initialized from a string of '0' and '1'.
+
+ Parameters
+ ----------
+ value: :class:`str`
+ Text string like '1010111'
+ """
+ try:
+ value = SizedInteger(value or '0', 2).setBitLength(len(value))
+
+ except ValueError:
+ raise error.PyAsn1Error('%s.fromBinaryString() error: %s' % (cls.__name__, sys.exc_info()[1]))
+
+ if prepend is not None:
+ value = SizedInteger(
+ (SizedInteger(prepend) << len(value)) | value
+ ).setBitLength(len(prepend) + len(value))
- def __add__(self, value): return self.clone(self._value + value)
- def __radd__(self, value): return self.clone(value + self._value)
- def __mul__(self, value): return self.clone(self._value * value)
- def __rmul__(self, value): return self * value
+ if not internalFormat:
+ value = cls(value)
+
+ return value
+
+ @classmethod
+ def fromOctetString(cls, value, internalFormat=False, prepend=None, padding=0):
+ """Create a |ASN.1| object initialized from a string.
+
+ Parameters
+ ----------
+ value: :class:`str` (Py2) or :class:`bytes` (Py3)
+ Text string like '\\\\x01\\\\xff' (Py2) or b'\\\\x01\\\\xff' (Py3)
+ """
+ value = SizedInteger(integer.from_bytes(value) >> padding).setBitLength(len(value) * 8 - padding)
+
+ if prepend is not None:
+ value = SizedInteger(
+ (SizedInteger(prepend) << len(value)) | value
+ ).setBitLength(len(prepend) + len(value))
+
+ if not internalFormat:
+ value = cls(value)
+
+ return value
def prettyIn(self, value):
- r = []
- if not value:
- return ()
- elif isinstance(value, str):
- if value[0] == '\'':
+ if isinstance(value, SizedInteger):
+ return value
+ elif octets.isStringType(value):
+ if not value:
+ return SizedInteger(0).setBitLength(0)
+
+ elif value[0] == '\'': # "'1011'B" -- ASN.1 schema representation (deprecated)
if value[-2:] == '\'B':
- for v in value[1:-2]:
- if v == '0':
- r.append(0)
- elif v == '1':
- r.append(1)
- else:
- raise error.PyAsn1Error(
- 'Non-binary BIT STRING initializer %s' % (v,)
- )
- return tuple(r)
+ return self.fromBinaryString(value[1:-2], internalFormat=True)
elif value[-2:] == '\'H':
- for v in value[1:-2]:
- i = 4
- v = int(v, 16)
- while i:
- i = i - 1
- r.append((v>>i)&0x01)
- return tuple(r)
+ return self.fromHexString(value[1:-2], internalFormat=True)
else:
raise error.PyAsn1Error(
'Bad BIT STRING value notation %s' % (value,)
- )
- else:
- for i in value.split(','):
- j = self.__namedValues.getValue(i)
- if j is None:
- raise error.PyAsn1Error(
- 'Unknown bit identifier \'%s\'' % (i,)
- )
- if j >= len(r):
- r.extend([0]*(j-len(r)+1))
- r[j] = 1
- return tuple(r)
+ )
+
+ elif self.namedValues and not value.isdigit(): # named bits like 'Urgent, Active'
+ names = [x.strip() for x in value.split(',')]
+
+ try:
+
+ bitPositions = [self.namedValues[name] for name in names]
+
+ except KeyError:
+ raise error.PyAsn1Error('unknown bit name(s) in %r' % (names,))
+
+ rightmostPosition = max(bitPositions)
+
+ number = 0
+ for bitPosition in bitPositions:
+ number |= 1 << (rightmostPosition - bitPosition)
+
+ return SizedInteger(number).setBitLength(rightmostPosition + 1)
+
+ elif value.startswith('0x'):
+ return self.fromHexString(value[2:], internalFormat=True)
+
+ elif value.startswith('0b'):
+ return self.fromBinaryString(value[2:], internalFormat=True)
+
+ else: # assume plain binary string like '1011'
+ return self.fromBinaryString(value, internalFormat=True)
+
elif isinstance(value, (tuple, list)):
- r = tuple(value)
- for b in r:
- if b and b != 1:
- raise error.PyAsn1Error(
- 'Non-binary BitString initializer \'%s\'' % (r,)
- )
- return r
+ return self.fromBinaryString(''.join([b and '1' or '0' for b in value]), internalFormat=True)
+
elif isinstance(value, BitString):
- return tuple(value)
+ return SizedInteger(value).setBitLength(len(value))
+
+ elif isinstance(value, intTypes):
+ return SizedInteger(value)
+
else:
raise error.PyAsn1Error(
'Bad BitString initializer type \'%s\'' % (value,)
- )
+ )
- def prettyOut(self, value):
- return '\"\'%s\'B\"' % ''.join([str(x) for x in value])
try:
+ # noinspection PyStatementEffect
all
+
except NameError: # Python 2.4
+ # noinspection PyShadowingBuiltins
def all(iterable):
for element in iterable:
if not element:
return False
return True
+
class OctetString(base.AbstractSimpleAsn1Item):
- tagSet = baseTagSet = tag.initTagSet(
+ """Create |ASN.1| schema or value object.
+
+ |ASN.1| objects are immutable and duck-type Python 2 :class:`str` or Python 3 :class:`bytes`.
+ When used in Unicode context, |ASN.1| type assumes "|encoding|" serialisation.
+
+ Keyword Args
+ ------------
+ value: :class:`str`, :class:`bytes` or |ASN.1| object
+ string (Python 2) or bytes (Python 3), alternatively unicode object
+ (Python 2) or string (Python 3) representing character string to be
+ serialised into octets (note `encoding` parameter) or |ASN.1| object.
+
+ tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+ Object representing non-default ASN.1 tag(s)
+
+ subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+ Object representing non-default ASN.1 subtype constraint(s)
+
+ encoding: :py:class:`str`
+ Unicode codec ID to encode/decode :class:`unicode` (Python 2) or
+ :class:`str` (Python 3) the payload when |ASN.1| object is used
+ in text string context.
+
+ binValue: :py:class:`str`
+ Binary string initializer to use instead of the *value*.
+ Example: '10110011'.
+
+ hexValue: :py:class:`str`
+ Hexadecimal string initializer to use instead of the *value*.
+ Example: 'DEADBEEF'.
+
+ Raises
+ ------
+ :py:class:`~pyasn1.error.PyAsn1Error`
+ On constraint violation or bad initializer.
+
+ Examples
+ --------
+ .. code-block:: python
+
+ class Icon(OctetString):
+ '''
+ ASN.1 specification:
+
+ Icon ::= OCTET STRING
+
+ icon1 Icon ::= '001100010011001000110011'B
+ icon2 Icon ::= '313233'H
+ '''
+ icon1 = Icon.fromBinaryString('001100010011001000110011')
+ icon2 = Icon.fromHexString('313233')
+ """
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+ #: associated with |ASN.1| type.
+ tagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x04)
- )
- defaultBinValue = defaultHexValue = base.noValue
- encoding = 'us-ascii'
- def __init__(self, value=None, tagSet=None, subtypeSpec=None,
- encoding=None, binValue=None, hexValue=None):
- if encoding is None:
- self._encoding = self.encoding
- else:
- self._encoding = encoding
- if binValue is not None:
- value = self.fromBinaryString(binValue)
- if hexValue is not None:
- value = self.fromHexString(hexValue)
- if value is None or value is base.noValue:
- value = self.defaultHexValue
- if value is None or value is base.noValue:
- value = self.defaultBinValue
- self.__asNumbersCache = None
- base.AbstractSimpleAsn1Item.__init__(self, value, tagSet, subtypeSpec)
-
- def clone(self, value=None, tagSet=None, subtypeSpec=None,
- encoding=None, binValue=None, hexValue=None):
- if value is None and tagSet is None and subtypeSpec is None and \
- encoding is None and binValue is None and hexValue is None:
- return self
- if value is None and binValue is None and hexValue is None:
- value = self._value
- if tagSet is None:
- tagSet = self._tagSet
- if subtypeSpec is None:
- subtypeSpec = self._subtypeSpec
- if encoding is None:
- encoding = self._encoding
- return self.__class__(
- value, tagSet, subtypeSpec, encoding, binValue, hexValue
- )
-
+ )
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+ #: imposing constraints on |ASN.1| type initialization values.
+ subtypeSpec = constraint.ConstraintsIntersection()
+
+ # Optimization for faster codec lookup
+ typeId = base.AbstractSimpleAsn1Item.getTypeId()
+
+ defaultBinValue = defaultHexValue = noValue
+ encoding = 'iso-8859-1'
+
+ def __init__(self, value=noValue, **kwargs):
+ if kwargs:
+ if value is noValue:
+ try:
+ value = self.fromBinaryString(kwargs.pop('binValue'))
+
+ except KeyError:
+ pass
+
+ try:
+ value = self.fromHexString(kwargs.pop('hexValue'))
+
+ except KeyError:
+ pass
+
+ if value is noValue:
+ if self.defaultBinValue is not noValue:
+ value = self.fromBinaryString(self.defaultBinValue)
+
+ elif self.defaultHexValue is not noValue:
+ value = self.fromHexString(self.defaultHexValue)
+
+ if 'encoding' not in kwargs:
+ kwargs['encoding'] = self.encoding
+
+ base.AbstractSimpleAsn1Item.__init__(self, value, **kwargs)
+
if sys.version_info[0] <= 2:
def prettyIn(self, value):
if isinstance(value, str):
return value
elif isinstance(value, unicode):
try:
- return value.encode(self._encoding)
+ return value.encode(self.encoding)
except (LookupError, UnicodeEncodeError):
raise error.PyAsn1Error(
- 'Can\'t encode string \'%s\' with \'%s\' codec' % (value, self._encoding)
+ "Can't encode string '%s' with codec %s" % (value, self.encoding)
)
elif isinstance(value, (tuple, list)):
try:
- return ''.join([ chr(x) for x in value ])
+ return ''.join([chr(x) for x in value])
except ValueError:
raise error.PyAsn1Error(
- 'Bad OctetString initializer \'%s\'' % (value,)
- )
+ "Bad %s initializer '%s'" % (self.__class__.__name__, value)
+ )
else:
return str(value)
+
+ def __str__(self):
+ return str(self._value)
+
+ def __unicode__(self):
+ try:
+ return self._value.decode(self.encoding)
+
+ except UnicodeDecodeError:
+ raise error.PyAsn1Error(
+ "Can't decode string '%s' with codec %s" % (self._value, self.encoding)
+ )
+
+ def asOctets(self):
+ return str(self._value)
+
+ def asNumbers(self):
+ return tuple([ord(x) for x in self._value])
+
else:
def prettyIn(self, value):
if isinstance(value, bytes):
return value
elif isinstance(value, str):
try:
- return value.encode(self._encoding)
+ return value.encode(self.encoding)
except UnicodeEncodeError:
raise error.PyAsn1Error(
- 'Can\'t encode string \'%s\' with \'%s\' codec' % (value, self._encoding)
+ "Can't encode string '%s' with '%s' codec" % (value, self.encoding)
)
- elif isinstance(value, OctetString):
+ elif isinstance(value, OctetString): # a shortcut, bytes() would work the same way
return value.asOctets()
- elif isinstance(value, (tuple, list, map)):
- try:
- return bytes(value)
- except ValueError:
- raise error.PyAsn1Error(
- 'Bad OctetString initializer \'%s\'' % (value,)
- )
+ elif isinstance(value, base.AbstractSimpleAsn1Item): # this mostly targets Integer objects
+ return self.prettyIn(str(value))
+ elif isinstance(value, (tuple, list)):
+ return self.prettyIn(bytes(value))
else:
- try:
- return str(value).encode(self._encoding)
- except UnicodeEncodeError:
- raise error.PyAsn1Error(
- 'Can\'t encode string \'%s\' with \'%s\' codec' % (value, self._encoding)
- )
-
+ return bytes(value)
+
+ def __str__(self):
+ try:
+ return self._value.decode(self.encoding)
- def fromBinaryString(self, value):
- bitNo = 8; byte = 0; r = ()
+ except UnicodeDecodeError:
+ raise error.PyAsn1Error(
+ "Can't decode string '%s' with '%s' codec at '%s'" % (self._value, self.encoding, self.__class__.__name__)
+ )
+
+ def __bytes__(self):
+ return bytes(self._value)
+
+ def asOctets(self):
+ return bytes(self._value)
+
+ def asNumbers(self):
+ return tuple(self._value)
+
+ #
+ # Normally, `.prettyPrint()` is called from `__str__()`. Historically,
+ # OctetString.prettyPrint() used to return hexified payload
+ # representation in cases when non-printable content is present. At the
+ # same time `str()` used to produce either octet-stream (Py2) or
+ # text (Py3) representations.
+ #
+ # Therefore `OctetString.__str__()` -> `.prettyPrint()` call chain is
+ # reversed to preserve the original behaviour.
+ #
+ # Eventually we should deprecate `.prettyPrint()` / `.prettyOut()` harness
+ # and end up with just `__str__()` producing hexified representation while
+ # both text and octet-stream representation should only be requested via
+ # the `.asOctets()` method.
+ #
+ # Note: ASN.1 OCTET STRING is never mean to contain text!
+ #
+
+ def prettyOut(self, value):
+ return value
+
+ def prettyPrint(self, scope=0):
+ # first see if subclass has its own .prettyOut()
+ value = self.prettyOut(self._value)
+
+ if value is not self._value:
+ return value
+
+ numbers = self.asNumbers()
+
+ for x in numbers:
+ # hexify if needed
+ if x < 32 or x > 126:
+ return '0x' + ''.join(('%.2x' % x for x in numbers))
+ else:
+ # this prevents infinite recursion
+ return OctetString.__str__(self)
+
+ @staticmethod
+ def fromBinaryString(value):
+ """Create a |ASN.1| object initialized from a string of '0' and '1'.
+
+ Parameters
+ ----------
+ value: :class:`str`
+ Text string like '1010111'
+ """
+ bitNo = 8
+ byte = 0
+ r = []
for v in value:
if bitNo:
- bitNo = bitNo - 1
+ bitNo -= 1
else:
bitNo = 7
- r = r + (byte,)
+ r.append(byte)
byte = 0
- if v == '0':
- v = 0
- elif v == '1':
- v = 1
+ if v in ('0', '1'):
+ v = int(v)
else:
raise error.PyAsn1Error(
'Non-binary OCTET STRING initializer %s' % (v,)
- )
- byte = byte | (v << bitNo)
- return octets.ints2octs(r + (byte,))
-
- def fromHexString(self, value):
- r = p = ()
+ )
+ byte |= v << bitNo
+
+ r.append(byte)
+
+ return octets.ints2octs(r)
+
+ @staticmethod
+ def fromHexString(value):
+ """Create a |ASN.1| object initialized from the hex string.
+
+ Parameters
+ ----------
+ value: :class:`str`
+ Text string like 'DEADBEEF'
+ """
+ r = []
+ p = []
for v in value:
if p:
- r = r + (int(p+v, 16),)
- p = ()
+ r.append(int(p + v, 16))
+ p = None
else:
p = v
if p:
- r = r + (int(p+'0', 16),)
- return octets.ints2octs(r)
+ r.append(int(p + '0', 16))
- def prettyOut(self, value):
- if sys.version_info[0] <= 2:
- numbers = tuple(( ord(x) for x in value ))
- else:
- numbers = tuple(value)
- if all(x >= 32 and x <= 126 for x in numbers):
- return str(value)
- else:
- return '0x' + ''.join(( '%.2x' % x for x in numbers ))
+ return octets.ints2octs(r)
- def __repr__(self):
- r = []
- doHex = False
- if self._value is not self.defaultValue:
- for x in self.asNumbers():
- if x < 32 or x > 126:
- doHex = True
- break
- if not doHex:
- r.append('%r' % (self._value,))
- if self._tagSet is not self.tagSet:
- r.append('tagSet=%r' % (self._tagSet,))
- if self._subtypeSpec is not self.subtypeSpec:
- r.append('subtypeSpec=%r' % (self._subtypeSpec,))
- if self.encoding is not self._encoding:
- r.append('encoding=%r' % (self._encoding,))
- if doHex:
- r.append('hexValue=%r' % ''.join([ '%.2x' % x for x in self.asNumbers() ]))
- return '%s(%s)' % (self.__class__.__name__, ', '.join(r))
-
- if sys.version_info[0] <= 2:
- def __str__(self): return str(self._value)
- def __unicode__(self):
- return self._value.decode(self._encoding, 'ignore')
- def asOctets(self): return self._value
- def asNumbers(self):
- if self.__asNumbersCache is None:
- self.__asNumbersCache = tuple([ ord(x) for x in self._value ])
- return self.__asNumbersCache
- else:
- def __str__(self): return self._value.decode(self._encoding, 'ignore')
- def __bytes__(self): return self._value
- def asOctets(self): return self._value
- def asNumbers(self):
- if self.__asNumbersCache is None:
- self.__asNumbersCache = tuple(self._value)
- return self.__asNumbersCache
-
# Immutable sequence object protocol
-
+
def __len__(self):
- if self._len is None:
- self._len = len(self._value)
- return self._len
+ return len(self._value)
+
def __getitem__(self, i):
- if isinstance(i, slice):
- return self.clone(operator.getitem(self._value, i))
+ if i.__class__ is slice:
+ return self.clone(self._value[i])
else:
return self._value[i]
- def __add__(self, value): return self.clone(self._value + self.prettyIn(value))
- def __radd__(self, value): return self.clone(self.prettyIn(value) + self._value)
- def __mul__(self, value): return self.clone(self._value * value)
- def __rmul__(self, value): return self * value
- def __int__(self): return int(self._value)
- def __float__(self): return float(self._value)
-
+ def __iter__(self):
+ return iter(self._value)
+
+ def __contains__(self, value):
+ return value in self._value
+
+ def __add__(self, value):
+ return self.clone(self._value + self.prettyIn(value))
+
+ def __radd__(self, value):
+ return self.clone(self.prettyIn(value) + self._value)
+
+ def __mul__(self, value):
+ return self.clone(self._value * value)
+
+ def __rmul__(self, value):
+ return self * value
+
+ def __int__(self):
+ return int(self._value)
+
+ def __float__(self):
+ return float(self._value)
+
+ def __reversed__(self):
+ return reversed(self._value)
+
+
class Null(OctetString):
- defaultValue = ''.encode() # This is tightly constrained
- tagSet = baseTagSet = tag.initTagSet(
+ """Create |ASN.1| schema or value object.
+
+ |ASN.1| objects are immutable and duck-type Python :class:`str` objects (always empty).
+
+ Keyword Args
+ ------------
+ value: :class:`str` or :py:class:`~pyasn1.type.univ.Null` object
+ Python empty string literal or any object that evaluates to `False`
+
+ tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+ Object representing non-default ASN.1 tag(s)
+
+ Raises
+ ------
+ :py:class:`~pyasn1.error.PyAsn1Error`
+ On constraint violation or bad initializer.
+
+ Examples
+ --------
+ .. code-block:: python
+
+ class Ack(Null):
+ '''
+ ASN.1 specification:
+
+ Ack ::= NULL
+ '''
+ ack = Ack('')
+ """
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+ #: associated with |ASN.1| type.
+ tagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x05)
- )
- subtypeSpec = OctetString.subtypeSpec+constraint.SingleValueConstraint(''.encode())
-
+ )
+ subtypeSpec = OctetString.subtypeSpec + constraint.SingleValueConstraint(octets.str2octs(''))
+
+ # Optimization for faster codec lookup
+ typeId = OctetString.getTypeId()
+
+ def prettyIn(self, value):
+ if value:
+ return value
+
+ return octets.str2octs('')
+
if sys.version_info[0] <= 2:
intTypes = (int, long)
else:
@@ -490,121 +1080,234 @@
numericTypes = intTypes + (float,)
+
class ObjectIdentifier(base.AbstractSimpleAsn1Item):
- tagSet = baseTagSet = tag.initTagSet(
+ """Create |ASN.1| schema or value object.
+
+ |ASN.1| objects are immutable and duck-type Python :class:`tuple` objects (tuple of non-negative integers).
+
+ Keyword Args
+ ------------
+ value: :class:`tuple`, :class:`str` or |ASN.1| object
+ Python sequence of :class:`int` or string literal or |ASN.1| object.
+
+ tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+ Object representing non-default ASN.1 tag(s)
+
+ subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+ Object representing non-default ASN.1 subtype constraint(s)
+
+ Raises
+ ------
+ :py:class:`~pyasn1.error.PyAsn1Error`
+ On constraint violation or bad initializer.
+
+ Examples
+ --------
+ .. code-block:: python
+
+ class ID(ObjectIdentifier):
+ '''
+ ASN.1 specification:
+
+ ID ::= OBJECT IDENTIFIER
+
+ id-edims ID ::= { joint-iso-itu-t mhs-motif(6) edims(7) }
+ id-bp ID ::= { id-edims 11 }
+ '''
+ id_edims = ID('2.6.7')
+ id_bp = id_edims + (11,)
+ """
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+ #: associated with |ASN.1| type.
+ tagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x06)
- )
- def __add__(self, other): return self.clone(self._value + other)
- def __radd__(self, other): return self.clone(other + self._value)
+ )
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+ #: imposing constraints on |ASN.1| type initialization values.
+ subtypeSpec = constraint.ConstraintsIntersection()
+
+ # Optimization for faster codec lookup
+ typeId = base.AbstractSimpleAsn1Item.getTypeId()
+
+ def __add__(self, other):
+ return self.clone(self._value + other)
+
+ def __radd__(self, other):
+ return self.clone(other + self._value)
+
+ def asTuple(self):
+ return self._value
- def asTuple(self): return self._value
-
# Sequence object protocol
-
+
def __len__(self):
- if self._len is None:
- self._len = len(self._value)
- return self._len
+ return len(self._value)
+
def __getitem__(self, i):
- if isinstance(i, slice):
- return self.clone(
- operator.getitem(self._value, i)
- )
+ if i.__class__ is slice:
+ return self.clone(self._value[i])
else:
return self._value[i]
- def __str__(self): return self.prettyPrint()
- def __repr__(self):
- return '%s(%r)' % (self.__class__.__name__, self.prettyPrint())
+ def __iter__(self):
+ return iter(self._value)
- def index(self, suboid): return self._value.index(suboid)
+ def __contains__(self, value):
+ return value in self._value
- def isPrefixOf(self, value):
- """Returns true if argument OID resides deeper in the OID tree"""
+ def index(self, suboid):
+ return self._value.index(suboid)
+
+ def isPrefixOf(self, other):
+ """Indicate if this |ASN.1| object is a prefix of other |ASN.1| object.
+
+ Parameters
+ ----------
+ other: |ASN.1| object
+ |ASN.1| object
+
+ Returns
+ -------
+ : :class:`bool`
+ :class:`True` if this |ASN.1| object is a parent (e.g. prefix) of the other |ASN.1| object
+ or :class:`False` otherwise.
+ """
l = len(self)
- if l <= len(value):
- if self._value[:l] == value[:l]:
- return 1
- return 0
+ if l <= len(other):
+ if self._value[:l] == other[:l]:
+ return True
+ return False
def prettyIn(self, value):
- """Dotted -> tuple of numerics OID converter"""
- if isinstance(value, tuple):
- pass
- elif isinstance(value, ObjectIdentifier):
- return tuple(value)
+ if isinstance(value, ObjectIdentifier):
+ return tuple(value)
elif octets.isStringType(value):
- r = []
- for element in [ x for x in value.split('.') if x != '' ]:
- try:
- r.append(int(element, 0))
- except ValueError:
- raise error.PyAsn1Error(
- 'Malformed Object ID %s at %s: %s' %
- (str(value), self.__class__.__name__, sys.exc_info()[1])
- )
- value = tuple(r)
- else:
+ if '-' in value:
+ raise error.PyAsn1Error(
+ 'Malformed Object ID %s at %s: %s' % (value, self.__class__.__name__, sys.exc_info()[1])
+ )
try:
- value = tuple(value)
- except TypeError:
+ return tuple([int(subOid) for subOid in value.split('.') if subOid])
+ except ValueError:
raise error.PyAsn1Error(
- 'Malformed Object ID %s at %s: %s' %
- (str(value), self.__class__.__name__,sys.exc_info()[1])
- )
+ 'Malformed Object ID %s at %s: %s' % (value, self.__class__.__name__, sys.exc_info()[1])
+ )
+
+ try:
+ tupleOfInts = tuple([int(subOid) for subOid in value if subOid >= 0])
+
+ except (ValueError, TypeError):
+ raise error.PyAsn1Error(
+ 'Malformed Object ID %s at %s: %s' % (value, self.__class__.__name__, sys.exc_info()[1])
+ )
+
+ if len(tupleOfInts) == len(value):
+ return tupleOfInts
+
+ raise error.PyAsn1Error('Malformed Object ID %s at %s' % (value, self.__class__.__name__))
+
+ def prettyOut(self, value):
+ return '.'.join([str(x) for x in value])
- for x in value:
- if not isinstance(x, intTypes) or x < 0:
- raise error.PyAsn1Error(
- 'Invalid sub-ID in %s at %s' % (value, self.__class__.__name__)
- )
-
- return value
- def prettyOut(self, value): return '.'.join([ str(x) for x in value ])
-
class Real(base.AbstractSimpleAsn1Item):
- binEncBase = None # binEncBase = 16 is recommended for large numbers
+ """Create |ASN.1| schema or value object.
+
+ |ASN.1| objects are immutable and duck-type Python :class:`float` objects.
+ Additionally, |ASN.1| objects behave like a :class:`tuple` in which case its
+ elements are mantissa, base and exponent.
+
+ Keyword Args
+ ------------
+ value: :class:`tuple`, :class:`float` or |ASN.1| object
+ Python sequence of :class:`int` (representing mantissa, base and
+ exponent) or float instance or *Real* class instance.
+
+ tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+ Object representing non-default ASN.1 tag(s)
+
+ subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+ Object representing non-default ASN.1 subtype constraint(s)
+
+ Raises
+ ------
+ :py:class:`~pyasn1.error.PyAsn1Error`
+ On constraint violation or bad initializer.
+
+ Examples
+ --------
+ .. code-block:: python
+
+ class Pi(Real):
+ '''
+ ASN.1 specification:
+
+ Pi ::= REAL
+
+ pi Pi ::= { mantissa 314159, base 10, exponent -5 }
+
+ '''
+ pi = Pi((314159, 10, -5))
+ """
+ binEncBase = None # binEncBase = 16 is recommended for large numbers
+
try:
_plusInf = float('inf')
_minusInf = float('-inf')
- _inf = (_plusInf, _minusInf)
+ _inf = _plusInf, _minusInf
+
except ValueError:
# Infinity support is platform and Python dependent
_plusInf = _minusInf = None
_inf = ()
- tagSet = baseTagSet = tag.initTagSet(
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+ #: associated with |ASN.1| type.
+ tagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x09)
- )
+ )
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+ #: imposing constraints on |ASN.1| type initialization values.
+ subtypeSpec = constraint.ConstraintsIntersection()
+
+ # Optimization for faster codec lookup
+ typeId = base.AbstractSimpleAsn1Item.getTypeId()
- def __normalizeBase10(self, value):
+ @staticmethod
+ def __normalizeBase10(value):
m, b, e = value
while m and m % 10 == 0:
- m = m / 10
- e = e + 1
+ m /= 10
+ e += 1
return m, b, e
def prettyIn(self, value):
if isinstance(value, tuple) and len(value) == 3:
- if not isinstance(value[0], numericTypes) or \
- not isinstance(value[1], intTypes) or \
- not isinstance(value[2], intTypes):
+ if (not isinstance(value[0], numericTypes) or
+ not isinstance(value[1], intTypes) or
+ not isinstance(value[2], intTypes)):
raise error.PyAsn1Error('Lame Real value syntax: %s' % (value,))
- if isinstance(value[0], float) and \
- self._inf and value[0] in self._inf:
+ if (isinstance(value[0], float) and
+ self._inf and value[0] in self._inf):
return value[0]
if value[1] not in (2, 10):
raise error.PyAsn1Error(
'Prohibited base for Real value: %s' % (value[1],)
- )
+ )
if value[1] == 10:
value = self.__normalizeBase10(value)
return value
elif isinstance(value, intTypes):
return self.__normalizeBase10((value, 10, 0))
- elif isinstance(value, (str, float)):
- if isinstance(value, str):
+ elif isinstance(value, float) or octets.isStringType(value):
+ if octets.isStringType(value):
try:
value = float(value)
except ValueError:
@@ -616,56 +1319,107 @@
else:
e = 0
while int(value) != value:
- value = value * 10
- e = e - 1
+ value *= 10
+ e -= 1
return self.__normalizeBase10((int(value), 10, e))
elif isinstance(value, Real):
return tuple(value)
raise error.PyAsn1Error(
'Bad real value syntax: %s' % (value,)
- )
-
- def prettyOut(self, value):
- if value in self._inf:
- return '\'%s\'' % value
- else:
- return str(value)
+ )
def prettyPrint(self, scope=0):
- if self.isInfinity():
- return self.prettyOut(self._value)
- else:
- return str(float(self))
+ try:
+ return self.prettyOut(float(self))
+
+ except OverflowError:
+ return '<overflow>'
+
+ @property
+ def isPlusInf(self):
+ """Indicate PLUS-INFINITY object value
+
+ Returns
+ -------
+ : :class:`bool`
+ :class:`True` if calling object represents plus infinity
+ or :class:`False` otherwise.
+
+ """
+ return self._value == self._plusInf
+
+ @property
+ def isMinusInf(self):
+ """Indicate MINUS-INFINITY object value
+
+ Returns
+ -------
+ : :class:`bool`
+ :class:`True` if calling object represents minus infinity
+ or :class:`False` otherwise.
+ """
+ return self._value == self._minusInf
+
+ @property
+ def isInf(self):
+ return self._value in self._inf
+
+ def __add__(self, value):
+ return self.clone(float(self) + value)
+
+ def __radd__(self, value):
+ return self + value
+
+ def __mul__(self, value):
+ return self.clone(float(self) * value)
+
+ def __rmul__(self, value):
+ return self * value
+
+ def __sub__(self, value):
+ return self.clone(float(self) - value)
+
+ def __rsub__(self, value):
+ return self.clone(value - float(self))
+
+ def __mod__(self, value):
+ return self.clone(float(self) % value)
+
+ def __rmod__(self, value):
+ return self.clone(value % float(self))
- def isPlusInfinity(self): return self._value == self._plusInf
- def isMinusInfinity(self): return self._value == self._minusInf
- def isInfinity(self): return self._value in self._inf
-
- def __str__(self): return str(float(self))
-
- def __add__(self, value): return self.clone(float(self) + value)
- def __radd__(self, value): return self + value
- def __mul__(self, value): return self.clone(float(self) * value)
- def __rmul__(self, value): return self * value
- def __sub__(self, value): return self.clone(float(self) - value)
- def __rsub__(self, value): return self.clone(value - float(self))
- def __mod__(self, value): return self.clone(float(self) % value)
- def __rmod__(self, value): return self.clone(value % float(self))
- def __pow__(self, value, modulo=None): return self.clone(pow(float(self), value, modulo))
- def __rpow__(self, value): return self.clone(pow(value, float(self)))
+ def __pow__(self, value, modulo=None):
+ return self.clone(pow(float(self), value, modulo))
+
+ def __rpow__(self, value):
+ return self.clone(pow(value, float(self)))
if sys.version_info[0] <= 2:
- def __div__(self, value): return self.clone(float(self) / value)
- def __rdiv__(self, value): return self.clone(value / float(self))
+ def __div__(self, value):
+ return self.clone(float(self) / value)
+
+ def __rdiv__(self, value):
+ return self.clone(value / float(self))
else:
- def __truediv__(self, value): return self.clone(float(self) / value)
- def __rtruediv__(self, value): return self.clone(value / float(self))
- def __divmod__(self, value): return self.clone(float(self) // value)
- def __rdivmod__(self, value): return self.clone(value // float(self))
+ def __truediv__(self, value):
+ return self.clone(float(self) / value)
+
+ def __rtruediv__(self, value):
+ return self.clone(value / float(self))
+
+ def __divmod__(self, value):
+ return self.clone(float(self) // value)
+
+ def __rdivmod__(self, value):
+ return self.clone(value // float(self))
+
+ def __int__(self):
+ return int(float(self))
- def __int__(self): return int(float(self))
if sys.version_info[0] <= 2:
- def __long__(self): return long(float(self))
+ def __long__(self):
+ return long(float(self))
+
def __float__(self):
if self._value in self._inf:
return self._value
@@ -673,31 +1427,58 @@
return float(
self._value[0] * pow(self._value[1], self._value[2])
)
- def __abs__(self): return self.clone(abs(float(self)))
- def __pos__(self): return self.clone(+float(self))
- def __neg__(self): return self.clone(-float(self))
+
+ def __abs__(self):
+ return self.clone(abs(float(self)))
+
+ def __pos__(self):
+ return self.clone(+float(self))
+
+ def __neg__(self):
+ return self.clone(-float(self))
+
def __round__(self, n=0):
r = round(float(self), n)
if n:
return self.clone(r)
else:
return r
- def __floor__(self): return self.clone(math.floor(float(self)))
- def __ceil__(self): return self.clone(math.ceil(float(self)))
+
+ def __floor__(self):
+ return self.clone(math.floor(float(self)))
+
+ def __ceil__(self):
+ return self.clone(math.ceil(float(self)))
+
if sys.version_info[0:2] > (2, 5):
- def __trunc__(self): return self.clone(math.trunc(float(self)))
+ def __trunc__(self):
+ return self.clone(math.trunc(float(self)))
+
+ def __lt__(self, value):
+ return float(self) < value
- def __lt__(self, value): return float(self) < value
- def __le__(self, value): return float(self) <= value
- def __eq__(self, value): return float(self) == value
- def __ne__(self, value): return float(self) != value
- def __gt__(self, value): return float(self) > value
- def __ge__(self, value): return float(self) >= value
+ def __le__(self, value):
+ return float(self) <= value
+
+ def __eq__(self, value):
+ return float(self) == value
+
+ def __ne__(self, value):
+ return float(self) != value
+
+ def __gt__(self, value):
+ return float(self) > value
+
+ def __ge__(self, value):
+ return float(self) >= value
if sys.version_info[0] <= 2:
- def __nonzero__(self): return bool(float(self))
+ def __nonzero__(self):
+ return bool(float(self))
else:
- def __bool__(self): return bool(float(self))
+ def __bool__(self):
+ return bool(float(self))
+
__hash__ = base.AbstractSimpleAsn1Item.__hash__
def __getitem__(self, idx):
@@ -705,421 +1486,1415 @@
raise error.PyAsn1Error('Invalid infinite value operation')
else:
return self._value[idx]
-
+
+ # compatibility stubs
+
+ def isPlusInfinity(self):
+ return self.isPlusInf
+
+ def isMinusInfinity(self):
+ return self.isMinusInf
+
+ def isInfinity(self):
+ return self.isInf
+
+
class Enumerated(Integer):
- tagSet = baseTagSet = tag.initTagSet(
+ """Create |ASN.1| type or object.
+
+ |ASN.1| objects are immutable and duck-type Python :class:`int` objects.
+
+ Keyword Args
+ ------------
+ value: :class:`int`, :class:`str` or |ASN.1| object
+ Python integer or string literal or |ASN.1| class instance.
+
+ tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+ Object representing non-default ASN.1 tag(s)
+
+ subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+ Object representing non-default ASN.1 subtype constraint(s)
+
+ namedValues: :py:class:`~pyasn1.type.namedval.NamedValues`
+ Object representing non-default symbolic aliases for numbers
+
+ Raises
+ ------
+ :py:class:`~pyasn1.error.PyAsn1Error`
+ On constraint violation or bad initializer.
+
+ Examples
+ --------
+
+ .. code-block:: python
+
+ class RadioButton(Enumerated):
+ '''
+ ASN.1 specification:
+
+ RadioButton ::= ENUMERATED { button1(0), button2(1),
+ button3(2) }
+
+ selected-by-default RadioButton ::= button1
+ '''
+ namedValues = NamedValues(
+ ('button1', 0), ('button2', 1),
+ ('button3', 2)
+ )
+
+ selected_by_default = RadioButton('button1')
+ """
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+ #: associated with |ASN.1| type.
+ tagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x0A)
- )
+ )
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+ #: imposing constraints on |ASN.1| type initialization values.
+ subtypeSpec = constraint.ConstraintsIntersection()
+
+ # Optimization for faster codec lookup
+ typeId = Integer.getTypeId()
+
+ #: Default :py:class:`~pyasn1.type.namedval.NamedValues` object
+ #: representing symbolic aliases for numbers
+ namedValues = namedval.NamedValues()
+
# "Structured" ASN.1 types
-class SetOf(base.AbstractConstructedAsn1Item):
- componentType = None
- tagSet = baseTagSet = tag.initTagSet(
- tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11)
- )
- typeId = 1
- strictConstraints = False
+class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item):
+ """Create |ASN.1| type.
+
+ |ASN.1| objects are mutable and duck-type Python :class:`list` objects.
+
+ Keyword Args
+ ------------
+ componentType : :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+ A pyasn1 object representing ASN.1 type allowed within |ASN.1| type
+
+ tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+ Object representing non-default ASN.1 tag(s)
+
+ subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+ Object representing non-default ASN.1 subtype constraint(s)
+
+ sizeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+ Object representing collection size constraint
+
+ Examples
+ --------
+
+ .. code-block:: python
+
+ class LotteryDraw(SequenceOf): # SetOf is similar
+ '''
+ ASN.1 specification:
+
+ LotteryDraw ::= SEQUENCE OF INTEGER
+ '''
+ componentType = Integer()
+
+ lotteryDraw = LotteryDraw()
+ lotteryDraw.extend([123, 456, 789])
+ """
+ def __init__(self, *args, **kwargs):
+ # support positional params for backward compatibility
+ if args:
+ for key, value in zip(('componentType', 'tagSet',
+ 'subtypeSpec', 'sizeSpec'), args):
+ if key in kwargs:
+ raise error.PyAsn1Error('Conflicting positional and keyword params!')
+ kwargs['componentType'] = value
+
+ base.AbstractConstructedAsn1Item.__init__(self, **kwargs)
+
+ # Python list protocol
+
+ def __getitem__(self, idx):
+ try:
+ return self.getComponentByPosition(idx)
+
+ except error.PyAsn1Error:
+ raise IndexError(sys.exc_info()[1])
+
+ def __setitem__(self, idx, value):
+ try:
+ self.setComponentByPosition(idx, value)
+
+ except error.PyAsn1Error:
+ raise IndexError(sys.exc_info()[1])
+
+ def clear(self):
+ self._componentValues = []
+
+ def append(self, value):
+ self[len(self)] = value
+
+ def count(self, value):
+ return self._componentValues.count(value)
+
+ def extend(self, values):
+ for value in values:
+ self.append(value)
+
+ def index(self, value, start=0, stop=None):
+ if stop is None:
+ stop = len(self)
+ try:
+ return self._componentValues.index(value, start, stop)
+
+ except error.PyAsn1Error:
+ raise ValueError(sys.exc_info()[1])
+
+ def reverse(self):
+ self._componentValues.reverse()
+
+ def sort(self, key=None, reverse=False):
+ self._componentValues.sort(key=key, reverse=reverse)
+
+ def __iter__(self):
+ return iter(self._componentValues)
+
+ def _cloneComponentValues(self, myClone, cloneValueFlag):
+ for idx, componentValue in enumerate(self._componentValues):
+ if componentValue is not noValue:
+ if isinstance(componentValue, base.AbstractConstructedAsn1Item):
+ myClone.setComponentByPosition(
+ idx, componentValue.clone(cloneValueFlag=cloneValueFlag)
+ )
+ else:
+ myClone.setComponentByPosition(idx, componentValue.clone())
+
+ def getComponentByPosition(self, idx, default=noValue, instantiate=True):
+ """Return |ASN.1| type component value by position.
+
+ Equivalent to Python sequence subscription operation (e.g. `[]`).
+
+ Parameters
+ ----------
+ idx : :class:`int`
+ Component index (zero-based). Must either refer to an existing
+ component or to N+1 component (if *componentType* is set). In the latter
+ case a new component type gets instantiated and appended to the |ASN.1|
+ sequence.
+
+ Keyword Args
+ ------------
+ default: :class:`object`
+ If set and requested component is a schema object, return the `default`
+ object instead of the requested component.
+
+ instantiate: :class:`bool`
+ If `True` (default), inner component will be automatically instantiated.
+ If 'False' either existing component or the `noValue` object will be
+ returned.
+
+ Returns
+ -------
+ : :py:class:`~pyasn1.type.base.PyAsn1Item`
+ Instantiate |ASN.1| component type or return existing component value
+
+ Examples
+ --------
+
+ .. code-block:: python
+
+ # can also be SetOf
+ class MySequenceOf(SequenceOf):
+ componentType = OctetString()
+
+ s = MySequenceOf()
+
+ # returns component #0 with `.isValue` property False
+ s.getComponentByPosition(0)
+
+ # returns None
+ s.getComponentByPosition(0, default=None)
+
+ s.clear()
+
+ # returns noValue
+ s.getComponentByPosition(0, instantiate=False)
+
+ # sets component #0 to OctetString() ASN.1 schema
+ # object and returns it
+ s.getComponentByPosition(0, instantiate=True)
+
+ # sets component #0 to ASN.1 value object
+ s.setComponentByPosition(0, 'ABCD')
+
+ # returns OctetString('ABCD') value object
+ s.getComponentByPosition(0, instantiate=False)
+
+ s.clear()
+
+ # returns noValue
+ s.getComponentByPosition(0, instantiate=False)
+ """
+ try:
+ componentValue = self._componentValues[idx]
+
+ except IndexError:
+ if not instantiate:
+ return default
+
+ self.setComponentByPosition(idx)
+
+ componentValue = self._componentValues[idx]
+
+ if default is noValue or componentValue.isValue:
+ return componentValue
+ else:
+ return default
+
+ def setComponentByPosition(self, idx, value=noValue,
+ verifyConstraints=True,
+ matchTags=True,
+ matchConstraints=True):
+ """Assign |ASN.1| type component by position.
+
+ Equivalent to Python sequence item assignment operation (e.g. `[]`)
+ or list.append() (when idx == len(self)).
+
+ Parameters
+ ----------
+ idx: :class:`int`
+ Component index (zero-based). Must either refer to existing
+ component or to N+1 component. In the latter case a new component
+ type gets instantiated (if *componentType* is set, or given ASN.1
+ object is taken otherwise) and appended to the |ASN.1| sequence.
+
+ Keyword Args
+ ------------
+ value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+ A Python value to initialize |ASN.1| component with (if *componentType* is set)
+ or ASN.1 value object to assign to |ASN.1| component.
+
+ verifyConstraints: :class:`bool`
+ If `False`, skip constraints validation
+
+ matchTags: :class:`bool`
+ If `False`, skip component tags matching
+
+ matchConstraints: :class:`bool`
+ If `False`, skip component constraints matching
+
+ Returns
+ -------
+ self
+
+ Raises
+ ------
+ IndexError:
+ When idx > len(self)
+ """
+ componentType = self.componentType
+
+ try:
+ currentValue = self._componentValues[idx]
+ except IndexError:
+ currentValue = noValue
+
+ if len(self._componentValues) < idx:
+ raise error.PyAsn1Error('Component index out of range')
- def _cloneComponentValues(self, myClone, cloneValueFlag):
- idx = 0; l = len(self._componentValues)
- while idx < l:
- c = self._componentValues[idx]
- if c is not None:
- if isinstance(c, base.AbstractConstructedAsn1Item):
- myClone.setComponentByPosition(
- idx, c.clone(cloneValueFlag=cloneValueFlag)
- )
- else:
- myClone.setComponentByPosition(idx, c.clone())
- idx = idx + 1
-
- def _verifyComponent(self, idx, value):
- t = self._componentType
- if t is None:
- return
- if not t.isSameTypeWith(value,matchConstraints=self.strictConstraints):
- raise error.PyAsn1Error('Component value is tag-incompatible: %r vs %r' % (value, t))
- if self.strictConstraints and \
- not t.isSuperTypeOf(value, matchTags=False):
- raise error.PyAsn1Error('Component value is constraints-incompatible: %r vs %r' % (value, t))
-
- def getComponentByPosition(self, idx): return self._componentValues[idx]
- def setComponentByPosition(self, idx, value=None, verifyConstraints=True):
- l = len(self._componentValues)
- if idx >= l:
- self._componentValues = self._componentValues + (idx-l+1)*[None]
- if value is None:
- if self._componentValues[idx] is None:
- if self._componentType is None:
- raise error.PyAsn1Error('Component type not defined')
- self._componentValues[idx] = self._componentType.clone()
- self._componentValuesSet = self._componentValuesSet + 1
- return self
- elif not isinstance(value, base.Asn1Item):
- if self._componentType is None:
+ if value is noValue:
+ if componentType is not None:
+ value = componentType.clone()
+ elif currentValue is noValue:
raise error.PyAsn1Error('Component type not defined')
- if isinstance(self._componentType, base.AbstractSimpleAsn1Item):
- value = self._componentType.clone(value=value)
+ elif not isinstance(value, base.Asn1Item):
+ if componentType is not None and isinstance(componentType, base.AbstractSimpleAsn1Item):
+ value = componentType.clone(value=value)
+ elif currentValue is not noValue and isinstance(currentValue, base.AbstractSimpleAsn1Item):
+ value = currentValue.clone(value=value)
else:
- raise error.PyAsn1Error('Instance value required')
- if verifyConstraints:
- if self._componentType is not None:
- self._verifyComponent(idx, value)
- self._verifySubtypeSpec(value, idx)
- if self._componentValues[idx] is None:
- self._componentValuesSet = self._componentValuesSet + 1
- self._componentValues[idx] = value
+ raise error.PyAsn1Error('Non-ASN.1 value %r and undefined component type at %r' % (value, self))
+ elif componentType is not None:
+ if self.strictConstraints:
+ if not componentType.isSameTypeWith(value, matchTags, matchConstraints):
+ raise error.PyAsn1Error('Component value is tag-incompatible: %r vs %r' % (value, componentType))
+ else:
+ if not componentType.isSuperTypeOf(value, matchTags, matchConstraints):
+ raise error.PyAsn1Error('Component value is tag-incompatible: %r vs %r' % (value, componentType))
+
+ if verifyConstraints and value.isValue:
+ try:
+ self.subtypeSpec(value, idx)
+
+ except error.PyAsn1Error:
+ exType, exValue, exTb = sys.exc_info()
+ raise exType('%s at %s' % (exValue, self.__class__.__name__))
+
+ if currentValue is noValue:
+ self._componentValues.append(value)
+ else:
+ self._componentValues[idx] = value
+
return self
- def getComponentTagMap(self):
- if self._componentType is not None:
- return self._componentType.getTagMap()
+ @property
+ def componentTagMap(self):
+ if self.componentType is not None:
+ return self.componentType.tagMap
def prettyPrint(self, scope=0):
- scope = scope + 1
- r = self.__class__.__name__ + ':\n'
- for idx in range(len(self._componentValues)):
- r = r + ' '*scope
- if self._componentValues[idx] is None:
- r = r + '<empty>'
+ scope += 1
+ representation = self.__class__.__name__ + ':\n'
+ for idx, componentValue in enumerate(self._componentValues):
+ representation += ' ' * scope
+ if (componentValue is noValue and
+ self.componentType is not None):
+ representation += '<empty>'
else:
- r = r + self._componentValues[idx].prettyPrint(scope)
- return r
+ representation += componentValue.prettyPrint(scope)
+ return representation
def prettyPrintType(self, scope=0):
- scope = scope + 1
- r = '%s -> %s {\n' % (self.getTagSet(), self.__class__.__name__)
- if self._componentType is not None:
- r = r + ' '*scope
- r = r + self._componentType.prettyPrintType(scope)
- return r + '\n' + ' '*(scope-1) + '}'
+ scope += 1
+ representation = '%s -> %s {\n' % (self.tagSet, self.__class__.__name__)
+ if self.componentType is not None:
+ representation += ' ' * scope
+ representation += self.componentType.prettyPrintType(scope)
+ return representation + '\n' + ' ' * (scope - 1) + '}'
+
+
+ @property
+ def isValue(self):
+ """Indicate that |ASN.1| object represents ASN.1 value.
+
+ If *isValue* is `False` then this object represents just ASN.1 schema.
+
+ If *isValue* is `True` then, in addition to its ASN.1 schema features,
+ this object can also be used like a Python built-in object (e.g. `int`,
+ `str`, `dict` etc.).
+
+ Returns
+ -------
+ : :class:`bool`
+ :class:`False` if object represents just ASN.1 schema.
+ :class:`True` if object represents ASN.1 schema and can be used as a normal value.
+
+ Note
+ ----
+ There is an important distinction between PyASN1 schema and value objects.
+ The PyASN1 schema objects can only participate in ASN.1 schema-related
+ operations (e.g. defining or testing the structure of the data). Most
+ obvious uses of ASN.1 schema is to guide serialisation codecs whilst
+ encoding/decoding serialised ASN.1 contents.
+
+ The PyASN1 value objects can **additionally** participate in many operations
+ involving regular Python objects (e.g. arithmetic, comprehension etc).
+ """
+ for componentValue in self._componentValues:
+ if componentValue is noValue or not componentValue.isValue:
+ return False
+
+ return True
-class SequenceOf(SetOf):
- tagSet = baseTagSet = tag.initTagSet(
+
+class SequenceOf(SequenceOfAndSetOfBase):
+ __doc__ = SequenceOfAndSetOfBase.__doc__
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+ #: associated with |ASN.1| type.
+ tagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10)
- )
- typeId = 2
+ )
+
+ #: Default :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+ #: object representing ASN.1 type allowed within |ASN.1| type
+ componentType = None
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+ #: imposing constraints on |ASN.1| type initialization values.
+ subtypeSpec = constraint.ConstraintsIntersection()
+
+ #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+ #: object imposing size constraint on |ASN.1| objects
+ sizeSpec = constraint.ConstraintsIntersection()
+
+ # Disambiguation ASN.1 types identification
+ typeId = SequenceOfAndSetOfBase.getTypeId()
+
+
+class SetOf(SequenceOfAndSetOfBase):
+ __doc__ = SequenceOfAndSetOfBase.__doc__
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+ #: associated with |ASN.1| type.
+ tagSet = tag.initTagSet(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11)
+ )
+
+ #: Default :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+ #: object representing ASN.1 type allowed within |ASN.1| type
+ componentType = None
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+ #: imposing constraints on |ASN.1| type initialization values.
+ subtypeSpec = constraint.ConstraintsIntersection()
+
+ #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+ #: object imposing size constraint on |ASN.1| objects
+ sizeSpec = constraint.ConstraintsIntersection()
+
+ # Disambiguation ASN.1 types identification
+ typeId = SequenceOfAndSetOfBase.getTypeId()
+
class SequenceAndSetBase(base.AbstractConstructedAsn1Item):
+ """Create |ASN.1| type.
+
+ |ASN.1| objects are mutable and duck-type Python :class:`dict` objects.
+
+ Keyword Args
+ ------------
+ componentType: :py:class:`~pyasn1.type.namedtype.NamedType`
+ Object holding named ASN.1 types allowed within this collection
+
+ tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+ Object representing non-default ASN.1 tag(s)
+
+ subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+ Object representing non-default ASN.1 subtype constraint(s)
+
+ sizeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+ Object representing collection size constraint
+
+ Examples
+ --------
+
+ .. code-block:: python
+
+ class Description(Sequence): # Set is similar
+ '''
+ ASN.1 specification:
+
+ Description ::= SEQUENCE {
+ surname IA5String,
+ first-name IA5String OPTIONAL,
+ age INTEGER DEFAULT 40
+ }
+ '''
+ componentType = NamedTypes(
+ NamedType('surname', IA5String()),
+ OptionalNamedType('first-name', IA5String()),
+ DefaultedNamedType('age', Integer(40))
+ )
+
+ descr = Description()
+ descr['surname'] = 'Smith'
+ descr['first-name'] = 'John'
+ """
+ #: Default :py:class:`~pyasn1.type.namedtype.NamedTypes`
+ #: object representing named ASN.1 types allowed within |ASN.1| type
componentType = namedtype.NamedTypes()
- strictConstraints = False
- def __init__(self, componentType=None, tagSet=None,
- subtypeSpec=None, sizeSpec=None):
- if componentType is None:
- componentType = self.componentType
- base.AbstractConstructedAsn1Item.__init__(
- self, componentType.clone(), tagSet, subtypeSpec, sizeSpec
- )
- self._componentTypeLen = len(self._componentType)
+
+
+ class DynamicNames(object):
+ """Fields names/positions mapping for component-less objects"""
+ def __init__(self):
+ self._keyToIdxMap = {}
+ self._idxToKeyMap = {}
+
+ def __len__(self):
+ return len(self._keyToIdxMap)
+
+ def __contains__(self, item):
+ return item in self._keyToIdxMap or item in self._idxToKeyMap
+
+ def __iter__(self):
+ return (self._idxToKeyMap[idx] for idx in range(len(self._idxToKeyMap)))
+
+ def __getitem__(self, item):
+ try:
+ return self._keyToIdxMap[item]
+
+ except KeyError:
+ return self._idxToKeyMap[item]
+
+ def getNameByPosition(self, idx):
+ try:
+ return self._idxToKeyMap[idx]
+
+ except KeyError:
+ raise error.PyAsn1Error('Type position out of range')
+
+ def getPositionByName(self, name):
+ try:
+ return self._keyToIdxMap[name]
+
+ except KeyError:
+ raise error.PyAsn1Error('Name %s not found' % (name,))
+
+ def addField(self, idx):
+ self._keyToIdxMap['field-%d' % idx] = idx
+ self._idxToKeyMap[idx] = 'field-%d' % idx
+
+
+ def __init__(self, **kwargs):
+ base.AbstractConstructedAsn1Item.__init__(self, **kwargs)
+ self._componentTypeLen = len(self.componentType)
+ self._dynamicNames = self._componentTypeLen or self.DynamicNames()
def __getitem__(self, idx):
- if isinstance(idx, str):
- return self.getComponentByName(idx)
+ if octets.isStringType(idx):
+ try:
+ return self.getComponentByName(idx)
+
+ except error.PyAsn1Error:
+ # duck-typing dict
+ raise KeyError(sys.exc_info()[1])
+
else:
- return base.AbstractConstructedAsn1Item.__getitem__(self, idx)
+ try:
+ return self.getComponentByPosition(idx)
+
+ except error.PyAsn1Error:
+ # duck-typing list
+ raise IndexError(sys.exc_info()[1])
def __setitem__(self, idx, value):
- if isinstance(idx, str):
- self.setComponentByName(idx, value)
+ if octets.isStringType(idx):
+ try:
+ self.setComponentByName(idx, value)
+
+ except error.PyAsn1Error:
+ # duck-typing dict
+ raise KeyError(sys.exc_info()[1])
+
+ else:
+ try:
+ self.setComponentByPosition(idx, value)
+
+ except error.PyAsn1Error:
+ # duck-typing list
+ raise IndexError(sys.exc_info()[1])
+
+ def __contains__(self, key):
+ if self._componentTypeLen:
+ return key in self.componentType
else:
- base.AbstractConstructedAsn1Item.__setitem__(self, idx, value)
-
+ return key in self._dynamicNames
+
+ def __iter__(self):
+ return iter(self.componentType or self._dynamicNames)
+
+ # Python dict protocol
+
+ def values(self):
+ for idx in range(self._componentTypeLen or len(self._dynamicNames)):
+ yield self[idx]
+
+ def keys(self):
+ return iter(self)
+
+ def items(self):
+ for idx in range(self._componentTypeLen or len(self._dynamicNames)):
+ if self._componentTypeLen:
+ yield self.componentType[idx].name, self[idx]
+ else:
+ yield self._dynamicNames[idx], self[idx]
+
+ def update(self, *iterValue, **mappingValue):
+ for k, v in iterValue:
+ self[k] = v
+ for k in mappingValue:
+ self[k] = mappingValue[k]
+
+ def clear(self):
+ self._componentValues = []
+ self._dynamicNames = self.DynamicNames()
+
def _cloneComponentValues(self, myClone, cloneValueFlag):
- idx = 0; l = len(self._componentValues)
- while idx < l:
- c = self._componentValues[idx]
- if c is not None:
- if isinstance(c, base.AbstractConstructedAsn1Item):
+ for idx, componentValue in enumerate(self._componentValues):
+ if componentValue is not noValue:
+ if isinstance(componentValue, base.AbstractConstructedAsn1Item):
myClone.setComponentByPosition(
- idx, c.clone(cloneValueFlag=cloneValueFlag)
- )
+ idx, componentValue.clone(cloneValueFlag=cloneValueFlag)
+ )
else:
- myClone.setComponentByPosition(idx, c.clone())
- idx = idx + 1
+ myClone.setComponentByPosition(idx, componentValue.clone())
+
+ def getComponentByName(self, name, default=noValue, instantiate=True):
+ """Returns |ASN.1| type component by name.
+
+ Equivalent to Python :class:`dict` subscription operation (e.g. `[]`).
+
+ Parameters
+ ----------
+ name: :class:`str`
+ |ASN.1| type component name
+
+ Keyword Args
+ ------------
+ default: :class:`object`
+ If set and requested component is a schema object, return the `default`
+ object instead of the requested component.
+
+ instantiate: :class:`bool`
+ If `True` (default), inner component will be automatically instantiated.
+ If 'False' either existing component or the `noValue` object will be
+ returned.
+
+ Returns
+ -------
+ : :py:class:`~pyasn1.type.base.PyAsn1Item`
+ Instantiate |ASN.1| component type or return existing component value
+ """
+ if self._componentTypeLen:
+ idx = self.componentType.getPositionByName(name)
+ else:
+ try:
+ idx = self._dynamicNames.getPositionByName(name)
+
+ except KeyError:
+ raise error.PyAsn1Error('Name %s not found' % (name,))
+
+ return self.getComponentByPosition(idx, default=default, instantiate=instantiate)
+
+ def setComponentByName(self, name, value=noValue,
+ verifyConstraints=True,
+ matchTags=True,
+ matchConstraints=True):
+ """Assign |ASN.1| type component by name.
+
+ Equivalent to Python :class:`dict` item assignment operation (e.g. `[]`).
+
+ Parameters
+ ----------
+ name: :class:`str`
+ |ASN.1| type component name
+
+ Keyword Args
+ ------------
+ value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+ A Python value to initialize |ASN.1| component with (if *componentType* is set)
+ or ASN.1 value object to assign to |ASN.1| component.
+
+ verifyConstraints: :class:`bool`
+ If `False`, skip constraints validation
+
+ matchTags: :class:`bool`
+ If `False`, skip component tags matching
+
+ matchConstraints: :class:`bool`
+ If `False`, skip component constraints matching
+
+ Returns
+ -------
+ self
+ """
+ if self._componentTypeLen:
+ idx = self.componentType.getPositionByName(name)
+ else:
+ try:
+ idx = self._dynamicNames.getPositionByName(name)
+
+ except KeyError:
+ raise error.PyAsn1Error('Name %s not found' % (name,))
- def _verifyComponent(self, idx, value):
- if idx >= self._componentTypeLen:
- raise error.PyAsn1Error(
- 'Component type error out of range'
- )
- t = self._componentType[idx].getType()
- if not t.isSameTypeWith(value,matchConstraints=self.strictConstraints):
- raise error.PyAsn1Error('Component value is tag-incompatible: %r vs %r' % (value, t))
- if self.strictConstraints and \
- not t.isSuperTypeOf(value, matchTags=False):
- raise error.PyAsn1Error('Component value is constraints-incompatible: %r vs %r' % (value, t))
-
- def getComponentByName(self, name):
- return self.getComponentByPosition(
- self._componentType.getPositionByName(name)
- )
- def setComponentByName(self, name, value=None, verifyConstraints=True):
return self.setComponentByPosition(
- self._componentType.getPositionByName(name),value,verifyConstraints
+ idx, value, verifyConstraints, matchTags, matchConstraints
)
- def getComponentByPosition(self, idx):
+ def getComponentByPosition(self, idx, default=noValue, instantiate=True):
+ """Returns |ASN.1| type component by index.
+
+ Equivalent to Python sequence subscription operation (e.g. `[]`).
+
+ Parameters
+ ----------
+ idx: :class:`int`
+ Component index (zero-based). Must either refer to an existing
+ component or (if *componentType* is set) new ASN.1 schema object gets
+ instantiated.
+
+ Keyword Args
+ ------------
+ default: :class:`object`
+ If set and requested component is a schema object, return the `default`
+ object instead of the requested component.
+
+ instantiate: :class:`bool`
+ If `True` (default), inner component will be automatically instantiated.
+ If 'False' either existing component or the `noValue` object will be
+ returned.
+
+ Returns
+ -------
+ : :py:class:`~pyasn1.type.base.PyAsn1Item`
+ a PyASN1 object
+
+ Examples
+ --------
+
+ .. code-block:: python
+
+ # can also be Set
+ class MySequence(Sequence):
+ componentType = NamedTypes(
+ NamedType('id', OctetString())
+ )
+
+ s = MySequence()
+
+ # returns component #0 with `.isValue` property False
+ s.getComponentByPosition(0)
+
+ # returns None
+ s.getComponentByPosition(0, default=None)
+
+ s.clear()
+
+ # returns noValue
+ s.getComponentByPosition(0, instantiate=False)
+
+ # sets component #0 to OctetString() ASN.1 schema
+ # object and returns it
+ s.getComponentByPosition(0, instantiate=True)
+
+ # sets component #0 to ASN.1 value object
+ s.setComponentByPosition(0, 'ABCD')
+
+ # returns OctetString('ABCD') value object
+ s.getComponentByPosition(0, instantiate=False)
+
+ s.clear()
+
+ # returns noValue
+ s.getComponentByPosition(0, instantiate=False)
+ """
try:
- return self._componentValues[idx]
+ componentValue = self._componentValues[idx]
+
except IndexError:
- if idx < self._componentTypeLen:
- return
- raise
- def setComponentByPosition(self, idx, value=None,
+ componentValue = noValue
+
+ if not instantiate:
+ if componentValue is noValue or not componentValue.isValue:
+ return default
+ else:
+ return componentValue
+
+ if componentValue is noValue:
+ self.setComponentByPosition(idx)
+
+ componentValue = self._componentValues[idx]
+
+ if default is noValue or componentValue.isValue:
+ return componentValue
+ else:
+ return default
+
+ def setComponentByPosition(self, idx, value=noValue,
verifyConstraints=True,
- exactTypes=False,
matchTags=True,
matchConstraints=True):
- l = len(self._componentValues)
- if idx >= l:
- self._componentValues = self._componentValues + (idx-l+1)*[None]
- if value is None:
- if self._componentValues[idx] is None:
- self._componentValues[idx] = self._componentType.getTypeByPosition(idx).clone()
- self._componentValuesSet = self._componentValuesSet + 1
- return self
+ """Assign |ASN.1| type component by position.
+
+ Equivalent to Python sequence item assignment operation (e.g. `[]`).
+
+ Parameters
+ ----------
+ idx : :class:`int`
+ Component index (zero-based). Must either refer to existing
+ component (if *componentType* is set) or to N+1 component
+ otherwise. In the latter case a new component of given ASN.1
+ type gets instantiated and appended to |ASN.1| sequence.
+
+ Keyword Args
+ ------------
+ value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+ A Python value to initialize |ASN.1| component with (if *componentType* is set)
+ or ASN.1 value object to assign to |ASN.1| component.
+
+ verifyConstraints : :class:`bool`
+ If `False`, skip constraints validation
+
+ matchTags: :class:`bool`
+ If `False`, skip component tags matching
+
+ matchConstraints: :class:`bool`
+ If `False`, skip component constraints matching
+
+ Returns
+ -------
+ self
+ """
+ componentType = self.componentType
+ componentTypeLen = self._componentTypeLen
+
+ try:
+ currentValue = self._componentValues[idx]
+
+ except IndexError:
+ currentValue = noValue
+ if componentTypeLen:
+ if componentTypeLen < idx:
+ raise error.PyAsn1Error('component index out of range')
+
+ self._componentValues = [noValue] * componentTypeLen
+
+ if value is noValue:
+ if componentTypeLen:
+ value = componentType.getTypeByPosition(idx).clone()
+
+ elif currentValue is noValue:
+ raise error.PyAsn1Error('Component type not defined')
+
elif not isinstance(value, base.Asn1Item):
- t = self._componentType.getTypeByPosition(idx)
- if isinstance(t, base.AbstractSimpleAsn1Item):
- value = t.clone(value=value)
+ if componentTypeLen:
+ subComponentType = componentType.getTypeByPosition(idx)
+ if isinstance(subComponentType, base.AbstractSimpleAsn1Item):
+ value = subComponentType.clone(value=value)
+
+ else:
+ raise error.PyAsn1Error('%s can cast only scalar values' % componentType.__class__.__name__)
+
+ elif currentValue is not noValue and isinstance(currentValue, base.AbstractSimpleAsn1Item):
+ value = currentValue.clone(value=value)
+
else:
- raise error.PyAsn1Error('Instance value required')
- if verifyConstraints:
- if self._componentTypeLen:
- self._verifyComponent(idx, value)
- self._verifySubtypeSpec(value, idx)
- if self._componentValues[idx] is None:
- self._componentValuesSet = self._componentValuesSet + 1
- self._componentValues[idx] = value
- return self
+ raise error.PyAsn1Error('%s undefined component type' % componentType.__class__.__name__)
- def getNameByPosition(self, idx):
- if self._componentTypeLen:
- return self._componentType.getNameByPosition(idx)
+ elif (matchTags or matchConstraints) and componentTypeLen:
+ subComponentType = componentType.getTypeByPosition(idx)
+ if subComponentType is not noValue:
+ subtypeChecker = (self.strictConstraints and
+ subComponentType.isSameTypeWith or
+ subComponentType.isSuperTypeOf)
+
+ if not subtypeChecker(value, matchTags, matchConstraints):
+ if not componentType[idx].openType:
+ raise error.PyAsn1Error('Component value is tag-incompatible: %r vs %r' % (value, componentType))
- def getDefaultComponentByPosition(self, idx):
- if self._componentTypeLen and self._componentType[idx].isDefaulted:
- return self._componentType[idx].getType()
+ if verifyConstraints and value.isValue:
+ try:
+ self.subtypeSpec(value, idx)
- def getComponentType(self):
- if self._componentTypeLen:
- return self._componentType
-
- def setDefaultComponents(self):
- if self._componentTypeLen == self._componentValuesSet:
- return
- idx = self._componentTypeLen
- while idx:
- idx = idx - 1
- if self._componentType[idx].isDefaulted:
- if self.getComponentByPosition(idx) is None:
- self.setComponentByPosition(idx)
- elif not self._componentType[idx].isOptional:
- if self.getComponentByPosition(idx) is None:
- raise error.PyAsn1Error(
- 'Uninitialized component #%s at %r' % (idx, self)
- )
+ except error.PyAsn1Error:
+ exType, exValue, exTb = sys.exc_info()
+ raise exType('%s at %s' % (exValue, self.__class__.__name__))
+
+ if componentTypeLen or idx in self._dynamicNames:
+ self._componentValues[idx] = value
+
+ elif len(self._componentValues) == idx:
+ self._componentValues.append(value)
+ self._dynamicNames.addField(idx)
+
+ else:
+ raise error.PyAsn1Error('Component index out of range')
+
+ return self
+
+ @property
+ def isValue(self):
+ """Indicate that |ASN.1| object represents ASN.1 value.
+
+ If *isValue* is `False` then this object represents just ASN.1 schema.
+
+ If *isValue* is `True` then, in addition to its ASN.1 schema features,
+ this object can also be used like a Python built-in object (e.g. `int`,
+ `str`, `dict` etc.).
+
+ Returns
+ -------
+ : :class:`bool`
+ :class:`False` if object represents just ASN.1 schema.
+ :class:`True` if object represents ASN.1 schema and can be used as a normal value.
+
+ Note
+ ----
+ There is an important distinction between PyASN1 schema and value objects.
+ The PyASN1 schema objects can only participate in ASN.1 schema-related
+ operations (e.g. defining or testing the structure of the data). Most
+ obvious uses of ASN.1 schema is to guide serialisation codecs whilst
+ encoding/decoding serialised ASN.1 contents.
+
+ The PyASN1 value objects can **additionally** participate in many operations
+ involving regular Python objects (e.g. arithmetic, comprehension etc).
+ """
+ componentType = self.componentType
+
+ if componentType:
+ for idx, subComponentType in enumerate(componentType.namedTypes):
+ if subComponentType.isDefaulted or subComponentType.isOptional:
+ continue
+
+ if not self._componentValues:
+ return False
+
+ componentValue = self._componentValues[idx]
+ if componentValue is noValue or not componentValue.isValue:
+ return False
+
+ else:
+ for componentValue in self._componentValues:
+ if componentValue is noValue or not componentValue.isValue:
+ return False
+
+ return True
def prettyPrint(self, scope=0):
- scope = scope + 1
- r = self.__class__.__name__ + ':\n'
- for idx in range(len(self._componentValues)):
- if self._componentValues[idx] is not None:
- r = r + ' '*scope
- componentType = self.getComponentType()
- if componentType is None:
- r = r + '<no-name>'
+ """Return an object representation string.
+
+ Returns
+ -------
+ : :class:`str`
+ Human-friendly object representation.
+ """
+ scope += 1
+ representation = self.__class__.__name__ + ':\n'
+ for idx, componentValue in enumerate(self._componentValues):
+ if componentValue is not noValue:
+ representation += ' ' * scope
+ if self.componentType:
+ representation += self.componentType.getNameByPosition(idx)
else:
- r = r + componentType.getNameByPosition(idx)
- r = '%s=%s\n' % (
- r, self._componentValues[idx].prettyPrint(scope)
- )
- return r
+ representation += self._dynamicNames.getNameByPosition(idx)
+ representation = '%s=%s\n' % (
+ representation, componentValue.prettyPrint(scope)
+ )
+ return representation
def prettyPrintType(self, scope=0):
- scope = scope + 1
- r = '%s -> %s {\n' % (self.getTagSet(), self.__class__.__name__)
- for idx in range(len(self.componentType)):
- r = r + ' '*scope
- r = r + '"%s"' % self.componentType.getNameByPosition(idx)
- r = '%s = %s\n' % (
- r, self._componentType.getTypeByPosition(idx).prettyPrintType(scope)
+ scope += 1
+ representation = '%s -> %s {\n' % (self.tagSet, self.__class__.__name__)
+ for idx, componentType in enumerate(self.componentType.values() or self._componentValues):
+ representation += ' ' * scope
+ if self.componentType:
+ representation += '"%s"' % self.componentType.getNameByPosition(idx)
+ else:
+ representation += '"%s"' % self._dynamicNames.getNameByPosition(idx)
+ representation = '%s = %s\n' % (
+ representation, componentType.prettyPrintType(scope)
)
- return r + '\n' + ' '*(scope-1) + '}'
+ return representation + '\n' + ' ' * (scope - 1) + '}'
+
+ # backward compatibility
+
+ def setDefaultComponents(self):
+ return self
+
+ def getComponentType(self):
+ if self._componentTypeLen:
+ return self.componentType
+
+ def getNameByPosition(self, idx):
+ if self._componentTypeLen:
+ return self.componentType[idx].name
+
class Sequence(SequenceAndSetBase):
- tagSet = baseTagSet = tag.initTagSet(
+ __doc__ = SequenceAndSetBase.__doc__
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+ #: associated with |ASN.1| type.
+ tagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10)
- )
- typeId = 3
+ )
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+ #: imposing constraints on |ASN.1| type initialization values.
+ subtypeSpec = constraint.ConstraintsIntersection()
+
+ #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+ #: object imposing constraints on |ASN.1| objects
+ sizeSpec = constraint.ConstraintsIntersection()
+
+ #: Default collection of ASN.1 types of component (e.g. :py:class:`~pyasn1.type.namedtype.NamedType`)
+ #: object imposing size constraint on |ASN.1| objects
+ componentType = namedtype.NamedTypes()
+
+ # Disambiguation ASN.1 types identification
+ typeId = SequenceAndSetBase.getTypeId()
+
+ # backward compatibility
def getComponentTagMapNearPosition(self, idx):
- if self._componentType:
- return self._componentType.getTagMapNearPosition(idx)
-
+ if self.componentType:
+ return self.componentType.getTagMapNearPosition(idx)
+
def getComponentPositionNearType(self, tagSet, idx):
- if self._componentType:
- return self._componentType.getPositionNearType(tagSet, idx)
+ if self.componentType:
+ return self.componentType.getPositionNearType(tagSet, idx)
else:
return idx
-
+
+
class Set(SequenceAndSetBase):
- tagSet = baseTagSet = tag.initTagSet(
+ __doc__ = SequenceAndSetBase.__doc__
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+ #: associated with |ASN.1| type.
+ tagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11)
- )
- typeId = 4
+ )
- def getComponent(self, innerFlag=0): return self
-
- def getComponentByType(self, tagSet, innerFlag=0):
- c = self.getComponentByPosition(
- self._componentType.getPositionByType(tagSet)
- )
- if innerFlag and isinstance(c, Set):
+ #: Default collection of ASN.1 types of component (e.g. :py:class:`~pyasn1.type.namedtype.NamedType`)
+ #: object representing ASN.1 type allowed within |ASN.1| type
+ componentType = namedtype.NamedTypes()
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+ #: imposing constraints on |ASN.1| type initialization values.
+ subtypeSpec = constraint.ConstraintsIntersection()
+
+ #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+ #: object imposing constraints on |ASN.1| objects
+ sizeSpec = constraint.ConstraintsIntersection()
+
+ # Disambiguation ASN.1 types identification
+ typeId = SequenceAndSetBase.getTypeId()
+
+ def getComponent(self, innerFlag=False):
+ return self
+
+ def getComponentByType(self, tagSet, default=noValue,
+ instantiate=True, innerFlag=False):
+ """Returns |ASN.1| type component by ASN.1 tag.
+
+ Parameters
+ ----------
+ tagSet : :py:class:`~pyasn1.type.tag.TagSet`
+ Object representing ASN.1 tags to identify one of
+ |ASN.1| object component
+
+ Keyword Args
+ ------------
+ default: :class:`object`
+ If set and requested component is a schema object, return the `default`
+ object instead of the requested component.
+
+ instantiate: :class:`bool`
+ If `True` (default), inner component will be automatically instantiated.
+ If 'False' either existing component or the `noValue` object will be
+ returned.
+
+ Returns
+ -------
+ : :py:class:`~pyasn1.type.base.PyAsn1Item`
+ a pyasn1 object
+ """
+ componentValue = self.getComponentByPosition(
+ self.componentType.getPositionByType(tagSet),
+ default=default, instantiate=instantiate
+ )
+ if innerFlag and isinstance(componentValue, Set):
# get inner component by inner tagSet
- return c.getComponent(1)
+ return componentValue.getComponent(innerFlag=True)
else:
# get outer component by inner tagSet
- return c
-
- def setComponentByType(self, tagSet, value=None, innerFlag=0,
- verifyConstraints=True):
- idx = self._componentType.getPositionByType(tagSet)
- t = self._componentType.getTypeByPosition(idx)
+ return componentValue
+
+ def setComponentByType(self, tagSet, value=noValue,
+ verifyConstraints=True,
+ matchTags=True,
+ matchConstraints=True,
+ innerFlag=False):
+ """Assign |ASN.1| type component by ASN.1 tag.
+
+ Parameters
+ ----------
+ tagSet : :py:class:`~pyasn1.type.tag.TagSet`
+ Object representing ASN.1 tags to identify one of
+ |ASN.1| object component
+
+ Keyword Args
+ ------------
+ value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+ A Python value to initialize |ASN.1| component with (if *componentType* is set)
+ or ASN.1 value object to assign to |ASN.1| component.
+
+ verifyConstraints : :class:`bool`
+ If `False`, skip constraints validation
+
+ matchTags: :class:`bool`
+ If `False`, skip component tags matching
+
+ matchConstraints: :class:`bool`
+ If `False`, skip component constraints matching
+
+ innerFlag: :class:`bool`
+ If `True`, search for matching *tagSet* recursively.
+
+ Returns
+ -------
+ self
+ """
+ idx = self.componentType.getPositionByType(tagSet)
+
if innerFlag: # set inner component by inner tagSet
- if t.getTagSet():
+ componentType = self.componentType.getTypeByPosition(idx)
+
+ if componentType.tagSet:
return self.setComponentByPosition(
- idx, value, verifyConstraints
+ idx, value, verifyConstraints, matchTags, matchConstraints
)
else:
- t = self.setComponentByPosition(idx).getComponentByPosition(idx)
- return t.setComponentByType(
- tagSet, value, innerFlag, verifyConstraints
+ componentType = self.getComponentByPosition(idx)
+ return componentType.setComponentByType(
+ tagSet, value, verifyConstraints, matchTags, matchConstraints, innerFlag=innerFlag
)
else: # set outer component by inner tagSet
return self.setComponentByPosition(
- idx, value, verifyConstraints
+ idx, value, verifyConstraints, matchTags, matchConstraints
)
-
- def getComponentTagMap(self):
- if self._componentType:
- return self._componentType.getTagMap(True)
-
- def getComponentPositionByType(self, tagSet):
- if self._componentType:
- return self._componentType.getPositionByType(tagSet)
+
+ @property
+ def componentTagMap(self):
+ if self.componentType:
+ return self.componentType.tagMapUnique
+
class Choice(Set):
- tagSet = baseTagSet = tag.TagSet() # untagged
+ """Create |ASN.1| type.
+
+ |ASN.1| objects are mutable and duck-type Python :class:`dict` objects.
+
+ Keyword Args
+ ------------
+ componentType: :py:class:`~pyasn1.type.namedtype.NamedType`
+ Object holding named ASN.1 types allowed within this collection
+
+ tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+ Object representing non-default ASN.1 tag(s)
+
+ subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+ Object representing non-default ASN.1 subtype constraint(s)
+
+ sizeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+ Object representing collection size constraint
+
+ Examples
+ --------
+
+ .. code-block:: python
+
+ class Afters(Choice):
+ '''
+ ASN.1 specification:
+
+ Afters ::= CHOICE {
+ cheese [0] IA5String,
+ dessert [1] IA5String
+ }
+ '''
+ componentType = NamedTypes(
+ NamedType('cheese', IA5String().subtype(
+ implicitTag=Tag(tagClassContext, tagFormatSimple, 0)
+ ),
+ NamedType('dessert', IA5String().subtype(
+ implicitTag=Tag(tagClassContext, tagFormatSimple, 1)
+ )
+ )
+
+ afters = Afters()
+ afters['cheese'] = 'Mascarpone'
+ """
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+ #: associated with |ASN.1| type.
+ tagSet = tag.TagSet() # untagged
+
+ #: Default collection of ASN.1 types of component (e.g. :py:class:`~pyasn1.type.namedtype.NamedType`)
+ #: object representing ASN.1 type allowed within |ASN.1| type
+ componentType = namedtype.NamedTypes()
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+ #: imposing constraints on |ASN.1| type initialization values.
+ subtypeSpec = constraint.ConstraintsIntersection()
+
+ #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+ #: object imposing size constraint on |ASN.1| objects
sizeSpec = constraint.ConstraintsIntersection(
constraint.ValueSizeConstraint(1, 1)
- )
- typeId = 5
+ )
+
+ # Disambiguation ASN.1 types identification
+ typeId = Set.getTypeId()
+
_currentIdx = None
def __eq__(self, other):
if self._componentValues:
return self._componentValues[self._currentIdx] == other
return NotImplemented
+
def __ne__(self, other):
if self._componentValues:
return self._componentValues[self._currentIdx] != other
return NotImplemented
+
def __lt__(self, other):
if self._componentValues:
return self._componentValues[self._currentIdx] < other
return NotImplemented
+
def __le__(self, other):
if self._componentValues:
return self._componentValues[self._currentIdx] <= other
return NotImplemented
+
def __gt__(self, other):
if self._componentValues:
return self._componentValues[self._currentIdx] > other
return NotImplemented
+
def __ge__(self, other):
if self._componentValues:
return self._componentValues[self._currentIdx] >= other
return NotImplemented
+
if sys.version_info[0] <= 2:
- def __nonzero__(self): return bool(self._componentValues)
+ def __nonzero__(self):
+ return self._componentValues and True or False
else:
- def __bool__(self): return bool(self._componentValues)
+ def __bool__(self):
+ return self._componentValues and True or False
+
+ def __len__(self):
+ return self._currentIdx is not None and 1 or 0
+
+ def __contains__(self, key):
+ if self._currentIdx is None:
+ return False
+ return key == self.componentType[self._currentIdx].getName()
+
+ def __iter__(self):
+ if self._currentIdx is None:
+ raise StopIteration
+ yield self.componentType[self._currentIdx].getName()
+
+ # Python dict protocol
+
+ def values(self):
+ if self._currentIdx is not None:
+ yield self._componentValues[self._currentIdx]
+
+ def keys(self):
+ if self._currentIdx is not None:
+ yield self.componentType[self._currentIdx].getName()
+
+ def items(self):
+ if self._currentIdx is not None:
+ yield self.componentType[self._currentIdx].getName(), self[self._currentIdx]
- def __len__(self): return self._currentIdx is not None and 1 or 0
-
def verifySizeSpec(self):
if self._currentIdx is None:
raise error.PyAsn1Error('Component not chosen')
- else:
- self._sizeSpec(' ')
def _cloneComponentValues(self, myClone, cloneValueFlag):
try:
- c = self.getComponent()
+ component = self.getComponent()
except error.PyAsn1Error:
pass
else:
- if isinstance(c, Choice):
- tagSet = c.getEffectiveTagSet()
+ if isinstance(component, Choice):
+ tagSet = component.effectiveTagSet
else:
- tagSet = c.getTagSet()
- if isinstance(c, base.AbstractConstructedAsn1Item):
+ tagSet = component.tagSet
+ if isinstance(component, base.AbstractConstructedAsn1Item):
myClone.setComponentByType(
- tagSet, c.clone(cloneValueFlag=cloneValueFlag)
- )
+ tagSet, component.clone(cloneValueFlag=cloneValueFlag)
+ )
else:
- myClone.setComponentByType(tagSet, c.clone())
+ myClone.setComponentByType(tagSet, component.clone())
- def setComponentByPosition(self, idx, value=None, verifyConstraints=True):
- l = len(self._componentValues)
- if idx >= l:
- self._componentValues = self._componentValues + (idx-l+1)*[None]
- if self._currentIdx is not None:
- self._componentValues[self._currentIdx] = None
- if value is None:
- if self._componentValues[idx] is None:
- self._componentValues[idx] = self._componentType.getTypeByPosition(idx).clone()
- self._componentValuesSet = 1
- self._currentIdx = idx
- return self
- elif not isinstance(value, base.Asn1Item):
- value = self._componentType.getTypeByPosition(idx).clone(
- value=value
- )
- if verifyConstraints:
- if self._componentTypeLen:
- self._verifyComponent(idx, value)
- self._verifySubtypeSpec(value, idx)
- self._componentValues[idx] = value
- self._currentIdx = idx
- self._componentValuesSet = 1
- return self
+ def getComponentByPosition(self, idx, default=noValue, instantiate=True):
+ __doc__ = Set.__doc__
- def getMinTagSet(self):
- if self._tagSet:
- return self._tagSet
- else:
- return self._componentType.genMinTagSet()
+ if self._currentIdx is None or self._currentIdx != idx:
+ return Set.getComponentByPosition(self, idx, default=default,
+ instantiate=instantiate)
- def getEffectiveTagSet(self):
- if self._tagSet:
- return self._tagSet
- else:
- c = self.getComponent()
- if isinstance(c, Choice):
- return c.getEffectiveTagSet()
- else:
- return c.getTagSet()
+ return self._componentValues[idx]
- def getTagMap(self):
- if self._tagSet:
- return Set.getTagMap(self)
- else:
- return Set.getComponentTagMap(self)
+ def setComponentByPosition(self, idx, value=noValue,
+ verifyConstraints=True,
+ matchTags=True,
+ matchConstraints=True):
+ """Assign |ASN.1| type component by position.
+
+ Equivalent to Python sequence item assignment operation (e.g. `[]`).
+
+ Parameters
+ ----------
+ idx: :class:`int`
+ Component index (zero-based). Must either refer to existing
+ component or to N+1 component. In the latter case a new component
+ type gets instantiated (if *componentType* is set, or given ASN.1
+ object is taken otherwise) and appended to the |ASN.1| sequence.
+
+ Keyword Args
+ ------------
+ value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
+ A Python value to initialize |ASN.1| component with (if *componentType* is set)
+ or ASN.1 value object to assign to |ASN.1| component. Once a new value is
+ set to *idx* component, previous value is dropped.
+
+ verifyConstraints : :class:`bool`
+ If `False`, skip constraints validation
+
+ matchTags: :class:`bool`
+ If `False`, skip component tags matching
+
+ matchConstraints: :class:`bool`
+ If `False`, skip component constraints matching
+
+ Returns
+ -------
+ self
+ """
+ oldIdx = self._currentIdx
+ Set.setComponentByPosition(self, idx, value, verifyConstraints, matchTags, matchConstraints)
+ self._currentIdx = idx
+ if oldIdx is not None and oldIdx != idx:
+ self._componentValues[oldIdx] = noValue
+ return self
- def getComponent(self, innerFlag=0):
+ @property
+ def effectiveTagSet(self):
+ """Return a :class:`~pyasn1.type.tag.TagSet` object of the currently initialized component or self (if |ASN.1| is tagged)."""
+ if self.tagSet:
+ return self.tagSet
+ else:
+ component = self.getComponent()
+ return component.effectiveTagSet
+
+ @property
+ def tagMap(self):
+ """"Return a :class:`~pyasn1.type.tagmap.TagMap` object mapping
+ ASN.1 tags to ASN.1 objects contained within callee.
+ """
+ if self.tagSet:
+ return Set.tagMap.fget(self)
+ else:
+ return self.componentType.tagMapUnique
+
+ def getComponent(self, innerFlag=False):
+ """Return currently assigned component of the |ASN.1| object.
+
+ Returns
+ -------
+ : :py:class:`~pyasn1.type.base.PyAsn1Item`
+ a PyASN1 object
+ """
if self._currentIdx is None:
raise error.PyAsn1Error('Component not chosen')
else:
@@ -1129,7 +2904,14 @@
else:
return c
- def getName(self, innerFlag=0):
+ def getName(self, innerFlag=False):
+ """Return the name of currently assigned component of the |ASN.1| object.
+
+ Returns
+ -------
+ : :py:class:`str`
+ |ASN.1| component name
+ """
if self._currentIdx is None:
raise error.PyAsn1Error('Component not chosen')
else:
@@ -1137,20 +2919,143 @@
c = self._componentValues[self._currentIdx]
if isinstance(c, Choice):
return c.getName(innerFlag)
- return self._componentType.getNameByPosition(self._currentIdx)
+ return self.componentType.getNameByPosition(self._currentIdx)
+
+ @property
+ def isValue(self):
+ """Indicate that |ASN.1| object represents ASN.1 value.
+
+ If *isValue* is `False` then this object represents just ASN.1 schema.
+
+ If *isValue* is `True` then, in addition to its ASN.1 schema features,
+ this object can also be used like a Python built-in object (e.g. `int`,
+ `str`, `dict` etc.).
+
+ Returns
+ -------
+ : :class:`bool`
+ :class:`False` if object represents just ASN.1 schema.
+ :class:`True` if object represents ASN.1 schema and can be used as a normal value.
+
+ Note
+ ----
+ There is an important distinction between PyASN1 schema and value objects.
+ The PyASN1 schema objects can only participate in ASN.1 schema-related
+ operations (e.g. defining or testing the structure of the data). Most
+ obvious uses of ASN.1 schema is to guide serialisation codecs whilst
+ encoding/decoding serialised ASN.1 contents.
+
+ The PyASN1 value objects can **additionally** participate in many operations
+ involving regular Python objects (e.g. arithmetic, comprehension etc).
+ """
+ if self._currentIdx is None:
+ return False
+
+ componentValue = self._componentValues[self._currentIdx]
+
+ return componentValue is not noValue and componentValue.isValue
+
+ def clear(self):
+ self._currentIdx = None
+ Set.clear(self)
+
+ # compatibility stubs
+
+ def getMinTagSet(self):
+ return self.minTagSet
- def setDefaultComponents(self): pass
class Any(OctetString):
- tagSet = baseTagSet = tag.TagSet() # untagged
- typeId = 6
+ """Create |ASN.1| schema or value object.
+
+ |ASN.1| objects are immutable and duck-type Python 2 :class:`str` or Python 3
+ :class:`bytes`. When used in Unicode context, |ASN.1| type assumes "|encoding|"
+ serialisation.
+
+ Keyword Args
+ ------------
+ value: :class:`str`, :class:`bytes` or |ASN.1| object
+ string (Python 2) or bytes (Python 3), alternatively unicode object
+ (Python 2) or string (Python 3) representing character string to be
+ serialised into octets (note `encoding` parameter) or |ASN.1| object.
+
+ tagSet: :py:class:`~pyasn1.type.tag.TagSet`
+ Object representing non-default ASN.1 tag(s)
+
+ subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
+ Object representing non-default ASN.1 subtype constraint(s)
+
+ encoding: :py:class:`str`
+ Unicode codec ID to encode/decode :class:`unicode` (Python 2) or
+ :class:`str` (Python 3) the payload when |ASN.1| object is used
+ in text string context.
+
+ binValue: :py:class:`str`
+ Binary string initializer to use instead of the *value*.
+ Example: '10110011'.
+
+ hexValue: :py:class:`str`
+ Hexadecimal string initializer to use instead of the *value*.
+ Example: 'DEADBEEF'.
+
+ Raises
+ ------
+ :py:class:`~pyasn1.error.PyAsn1Error`
+ On constraint violation or bad initializer.
+
+ Examples
+ --------
+ .. code-block:: python
+
+ class Error(Sequence):
+ '''
+ ASN.1 specification:
+
+ Error ::= SEQUENCE {
+ code INTEGER,
+ parameter ANY DEFINED BY code -- Either INTEGER or REAL
+ }
+ '''
+ componentType=NamedTypes(
+ NamedType('code', Integer()),
+ NamedType('parameter', Any(),
+ openType=OpenType('code', {1: Integer(),
+ 2: Real()}))
+ )
+
+ error = Error()
+ error['code'] = 1
+ error['parameter'] = Integer(1234)
+ """
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
+ #: associated with |ASN.1| type.
+ tagSet = tag.TagSet() # untagged
+
+ #: Set (on class, not on instance) or return a
+ #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
+ #: imposing constraints on |ASN.1| type initialization values.
+ subtypeSpec = constraint.ConstraintsIntersection()
+
+ # Disambiguation ASN.1 types identification
+ typeId = OctetString.getTypeId()
+
+ @property
+ def tagMap(self):
+ """"Return a :class:`~pyasn1.type.tagmap.TagMap` object mapping
+ ASN.1 tags to ASN.1 objects contained within callee.
+ """
+ try:
+ return self._tagMap
- def getTagMap(self):
- return tagmap.TagMap(
- { self.getTagSet(): self },
- { eoo.endOfOctets.getTagSet(): eoo.endOfOctets },
- self
+ except AttributeError:
+ self._tagMap = tagmap.TagMap(
+ {self.tagSet: self},
+ {eoo.endOfOctets.tagSet: eoo.endOfOctets},
+ self
)
+ return self._tagMap
+
# XXX
# coercion rules?
diff -u -r 1.9.67/google_appengine/lib/pyasn1/pyasn1/type/useful.py 1.9.68/google_appengine/lib/pyasn1/pyasn1/type/useful.py
--- 1.9.67/google_appengine/lib/pyasn1/pyasn1/type/useful.py 2018-03-09 20:05:30.000000000 -0800
+++ 1.9.68/google_appengine/lib/pyasn1/pyasn1/type/useful.py 2018-04-03 21:35:14.000000000 -0700
@@ -1,17 +1,191 @@
-# ASN.1 "useful" types
-from pyasn1.type import char, tag
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2017, Ilya Etingof <[email protected]>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import datetime
+
+from pyasn1 import error
+from pyasn1.compat import dateandtime
+from pyasn1.compat import string
+from pyasn1.type import char
+from pyasn1.type import tag
+from pyasn1.type import univ
+
+__all__ = ['ObjectDescriptor', 'GeneralizedTime', 'UTCTime']
+
+NoValue = univ.NoValue
+noValue = univ.noValue
+
class ObjectDescriptor(char.GraphicString):
+ __doc__ = char.GraphicString.__doc__
+
+ #: Default :py:class:`~pyasn1.type.tag.TagSet` object for |ASN.1| objects
tagSet = char.GraphicString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 7)
- )
+ )
+
+ # Optimization for faster codec lookup
+ typeId = char.GraphicString.getTypeId()
+
+
+class TimeMixIn(object):
+
+ _yearsDigits = 4
+ _hasSubsecond = False
+ _optionalMinutes = False
+ _shortTZ = False
+
+ class FixedOffset(datetime.tzinfo):
+ """Fixed offset in minutes east from UTC."""
+
+ # defaulted arguments required
+ # https: // docs.python.org / 2.3 / lib / datetime - tzinfo.html
+ def __init__(self, offset=0, name='UTC'):
+ self.__offset = datetime.timedelta(minutes=offset)
+ self.__name = name
+
+ def utcoffset(self, dt):
+ return self.__offset
+
+ def tzname(self, dt):
+ return self.__name
+
+ def dst(self, dt):
+ return datetime.timedelta(0)
+
+ UTC = FixedOffset()
+
+ @property
+ def asDateTime(self):
+ """Create :py:class:`datetime.datetime` object from a |ASN.1| object.
+
+ Returns
+ -------
+ :
+ new instance of :py:class:`datetime.datetime` object
+ """
+ text = str(self)
+ if text.endswith('Z'):
+ tzinfo = TimeMixIn.UTC
+ text = text[:-1]
+
+ elif '-' in text or '+' in text:
+ if '+' in text:
+ text, plusminus, tz = string.partition(text, '+')
+ else:
+ text, plusminus, tz = string.partition(text, '-')
+
+ if self._shortTZ and len(tz) == 2:
+ tz += '00'
+
+ if len(tz) != 4:
+ raise error.PyAsn1Error('malformed time zone offset %s' % tz)
+
+ try:
+ minutes = int(tz[:2]) * 60 + int(tz[2:])
+ if plusminus == '-':
+ minutes *= -1
+
+ except ValueError:
+ raise error.PyAsn1Error('unknown time specification %s' % self)
+
+ tzinfo = TimeMixIn.FixedOffset(minutes, '?')
-class GeneralizedTime(char.VisibleString):
+ else:
+ tzinfo = None
+
+ if '.' in text or ',' in text:
+ if '.' in text:
+ text, _, ms = string.partition(text, '.')
+ else:
+ text, _, ms = string.partition(text, ',')
+
+ try:
+ ms = int(ms) * 10000
+
+ except ValueError:
+ raise error.PyAsn1Error('bad sub-second time specification %s' % self)
+
+ else:
+ ms = 0
+
+ if self._optionalMinutes and len(text) - self._yearsDigits == 6:
+ text += '0000'
+ elif len(text) - self._yearsDigits == 8:
+ text += '00'
+
+ try:
+ dt = dateandtime.strptime(text, self._yearsDigits == 4 and '%Y%m%d%H%M%S' or '%y%m%d%H%M%S')
+
+ except ValueError:
+ raise error.PyAsn1Error('malformed datetime format %s' % self)
+
+ return dt.replace(microsecond=ms, tzinfo=tzinfo)
+
+ @classmethod
+ def fromDateTime(cls, dt):
+ """Create |ASN.1| object from a :py:class:`datetime.datetime` object.
+
+ Parameters
+ ----------
+ dt: :py:class:`datetime.datetime` object
+ The `datetime.datetime` object to initialize the |ASN.1| object
+ from
+
+ Returns
+ -------
+ :
+ new instance of |ASN.1| value
+ """
+ text = dt.strftime(cls._yearsDigits == 4 and '%Y%m%d%H%M%S' or '%y%m%d%H%M%S')
+ if cls._hasSubsecond:
+ text += '.%d' % (dt.microsecond // 10000)
+
+ if dt.utcoffset():
+ seconds = dt.utcoffset().seconds
+ if seconds < 0:
+ text += '-'
+ else:
+ text += '+'
+ text += '%.2d%.2d' % (seconds // 3600, seconds % 3600)
+ else:
+ text += 'Z'
+
+ return cls(text)
+
+
+class GeneralizedTime(char.VisibleString, TimeMixIn):
+ __doc__ = char.VisibleString.__doc__
+
+ #: Default :py:class:`~pyasn1.type.tag.TagSet` object for |ASN.1| objects
tagSet = char.VisibleString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 24)
- )
+ )
+
+ # Optimization for faster codec lookup
+ typeId = char.VideotexString.getTypeId()
-class UTCTime(char.VisibleString):
+ _yearsDigits = 4
+ _hasSubsecond = True
+ _optionalMinutes = True
+ _shortTZ = True
+
+
+class UTCTime(char.VisibleString, TimeMixIn):
+ __doc__ = char.VisibleString.__doc__
+
+ #: Default :py:class:`~pyasn1.type.tag.TagSet` object for |ASN.1| objects
tagSet = char.VisibleString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 23)
- )
+ )
+
+ # Optimization for faster codec lookup
+ typeId = char.VideotexString.getTypeId()
+
+ _yearsDigits = 2
+ _hasSubsecond = False
+ _optionalMinutes = False
+ _shortTZ = False
diff -u -r 1.9.67/google_appengine/php_cli.py 1.9.68/google_appengine/php_cli.py
--- 1.9.67/google_appengine/php_cli.py 2018-03-09 20:05:30.000000000 -0800
+++ 1.9.68/google_appengine/php_cli.py 2018-04-03 21:35:14.000000000 -0700
@@ -82,13 +82,6 @@
"""
script_name = os.path.basename(file_path)
-
-
-
-
- if '--api_server_supports_grpc' in sys.argv:
- _PATHS.add_grpc_path(script_name)
-
sys.path = (_PATHS.script_paths(script_name) +
_PATHS.scrub_path(script_name, sys.path))
diff -u -r 1.9.67/google_appengine/wrapper_util.py 1.9.68/google_appengine/wrapper_util.py
--- 1.9.67/google_appengine/wrapper_util.py 2018-03-09 20:05:30.000000000 -0800
+++ 1.9.68/google_appengine/wrapper_util.py 2018-04-03 21:35:14.000000000 -0700
@@ -229,6 +229,12 @@
os.path.join(dir_path, 'lib', 'jinja2-2.6'),
os.path.join(dir_path, 'lib', 'webob-1.2.3'),
os.path.join(dir_path, 'lib', 'webapp2-2.5.1'),
+
+
+
+
+
+ os.path.join(dir_path, 'lib', 'grpcio-1.9.1'),
]
@@ -360,18 +366,3 @@
return [path for path in paths
if os.path.normcase(path) not in sys_paths_to_scrub]
-
-
-
-
-
-
- def add_grpc_path(self, script_name):
- """Adds grpcio-1.0.0 to sys.path and avoid hard-coding.
-
- Args:
- script_name: the basename of the script, for example 'appcfg.py'.
- """
-
- grpc_lib_path = os.path.join(self.dir_path, 'lib', 'grpcio-1.0.0')
- self._script_to_paths[script_name].append(grpc_lib_path)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment