def __init__(self, session=None, connection_factory=None, resource_factory=None, collection_factory=None): """ Creates a ``Session`` instance. :param session: (Optional) Custom instantiated ``botocore`` instance. Useful if you have specific needs. If not present, a default ``Session`` will be created. :type session: <botocore.session.Session> instance :param connection_factory: (Optional) Specifies a custom ``ConnectionFactory`` to be used. Useful if you need to change how ``Connection`` objects are constructed by the session. :type connection_factory: <kotocore.connection.ConnectionFactory> instance :param resource_factory: (Optional) Specifies a custom ``ResourceFactory`` to be used. Useful if you need to change how ``Resource`` objects are constructed by the session. :type resource_factory: <kotocore.resources.ResourceFactory> instance :param collection_factory: (Optional) Specifies a custom ``CollectionFactory`` to be used. Useful if you need to change how ``Collection`` objects are constructed by the session. :type collection_factory: <kotocore.collections.CollectionFactory> instance """ super(Session, self).__init__() self.core_session = session self.connection_factory = connection_factory self.resource_factory = resource_factory self.collection_factory = collection_factory self.cache = self.cache_class() if not self.core_session: self.core_session = botocore.session.get_session() self.core_session.user_agent_name = USER_AGENT_NAME self.core_session.user_agent_version = USER_AGENT_VERSION if not self.connection_factory: from kotocore.connection import ConnectionFactory self.connection_factory = ConnectionFactory(session=self) if not self.resource_factory: from kotocore.resources import ResourceFactory self.resource_factory = ResourceFactory(session=self) if not self.collection_factory: from kotocore.collections import CollectionFactory self.collection_factory = CollectionFactory(session=self)
def setUp(self): super(CollectionFactoryTestCase, self).setUp() self.session = Session(FakeSession(TestCoreService())) self.test_dirs = [ os.path.join(os.path.dirname(__file__), 'test_data') ] self.test_loader = ResourceJSONLoader(self.test_dirs) self.cd = CollectionDetails( self.session, 'test', 'PipelineCollection', loader=self.test_loader ) self.cf = CollectionFactory( session=self.session, loader=self.test_loader ) # Fake in the class. self.session.cache.set_resource('test', 'Pipeline', FakePipeResource)
class Session(object): """ Stores all the state for a given ``kotocore`` session. Can dynamically create all the various ``Connection`` classes. Usage:: >>> from kotocore.session import Session >>> session = Session() >>> sqs_conn = session.connect_to('sqs', region_name='us-west-2') """ cache_class = ServiceCache def __init__(self, session=None, connection_factory=None, resource_factory=None, collection_factory=None): """ Creates a ``Session`` instance. :param session: (Optional) Custom instantiated ``botocore`` instance. Useful if you have specific needs. If not present, a default ``Session`` will be created. :type session: <botocore.session.Session> instance :param connection_factory: (Optional) Specifies a custom ``ConnectionFactory`` to be used. Useful if you need to change how ``Connection`` objects are constructed by the session. :type connection_factory: <kotocore.connection.ConnectionFactory> instance :param resource_factory: (Optional) Specifies a custom ``ResourceFactory`` to be used. Useful if you need to change how ``Resource`` objects are constructed by the session. :type resource_factory: <kotocore.resources.ResourceFactory> instance :param collection_factory: (Optional) Specifies a custom ``CollectionFactory`` to be used. Useful if you need to change how ``Collection`` objects are constructed by the session. :type collection_factory: <kotocore.collections.CollectionFactory> instance """ super(Session, self).__init__() self.core_session = session self.connection_factory = connection_factory self.resource_factory = resource_factory self.collection_factory = collection_factory self.cache = self.cache_class() if not self.core_session: self.core_session = botocore.session.get_session() self.core_session.user_agent_name = USER_AGENT_NAME self.core_session.user_agent_version = USER_AGENT_VERSION if not self.connection_factory: from kotocore.connection import ConnectionFactory self.connection_factory = ConnectionFactory(session=self) if not self.resource_factory: from kotocore.resources import ResourceFactory self.resource_factory = ResourceFactory(session=self) if not self.collection_factory: from kotocore.collections import CollectionFactory self.collection_factory = CollectionFactory(session=self) def get_connection(self, service_name): """ Returns a ``Connection`` **class** for a given service. :param service_name: A string that specifies the name of the desired service. Ex. ``sqs``, ``sns``, ``dynamodb``, etc. :type service_name: string :rtype: <kotocore.connection.Connection subclass> """ try: return self.cache.get_connection(service_name) except NotCached: pass # We didn't find it. Construct it. new_class = self.connection_factory.construct_for(service_name) self.cache.set_connection(service_name, new_class) return new_class def get_resource(self, service_name, resource_name, base_class=None): """ Returns a ``Resource`` **class** for a given service. :param service_name: A string that specifies the name of the desired service. Ex. ``sqs``, ``sns``, ``dynamodb``, etc. :type service_name: string :param resource_name: A string that specifies the name of the desired class. Ex. ``Queue``, ``Notification``, ``Table``, etc. :type resource_name: string :param base_class: (Optional) The base class of the object. Prevents "magically" loading the wrong class (one with a different base). :type base_class: class :rtype: <kotocore.resources.Resource subclass> """ try: return self.cache.get_resource(service_name, resource_name, base_class=base_class) except NotCached: pass # We didn't find it. Construct it. new_class = self.resource_factory.construct_for(service_name, resource_name, base_class=base_class) self.cache.set_resource(service_name, resource_name, new_class) return new_class def get_collection(self, service_name, collection_name, base_class=None): """ Returns a ``Collection`` **class** for a given service. :param service_name: A string that specifies the name of the desired service. Ex. ``sqs``, ``sns``, ``dynamodb``, etc. :type service_name: string :param collection_name: A string that specifies the name of the desired class. Ex. ``QueueCollection``, ``NotificationCollection``, ``TableCollection``, etc. :type collection_name: string :param base_class: (Optional) The base class of the object. Prevents "magically" loading the wrong class (one with a different base). :type base_class: class :rtype: <kotocore.collections.Collection subclass> """ try: return self.cache.get_collection(service_name, collection_name, base_class=base_class) except NotCached: pass # We didn't find it. Construct it. new_class = self.collection_factory.construct_for(service_name, collection_name, base_class=base_class) self.cache.set_collection(service_name, collection_name, new_class) return new_class def connect_to(self, service_name, **kwargs): """ Shortcut method to make instantiating the ``Connection`` classes easier. Forwards ``**kwargs`` like region, keys, etc. on to the constructor. :param service_name: A string that specifies the name of the desired service. Ex. ``sqs``, ``sns``, ``dynamodb``, etc. :type service_name: string :rtype: <kotocore.connection.Connection> instance """ service_class = self.get_connection(service_name) return service_class.connect_to(**kwargs) def get_core_service(self, service_name): """ Returns a ``botocore.service.Service``. Mostly an abstraction for the ``*Connection`` objects to get what they need for introspection. :param service_name: A string that specifies the name of the desired service. Ex. ``sqs``, ``sns``, ``dynamodb``, etc. :type service_name: string :rtype: <botocore.service.Service subclass> """ return self.core_session.get_service(service_name)
class CollectionFactoryTestCase(unittest.TestCase): def setUp(self): super(CollectionFactoryTestCase, self).setUp() self.session = Session(FakeSession(TestCoreService())) self.test_dirs = [ os.path.join(os.path.dirname(__file__), 'test_data') ] self.test_loader = ResourceJSONLoader(self.test_dirs) self.cd = CollectionDetails( self.session, 'test', 'PipelineCollection', loader=self.test_loader ) self.cf = CollectionFactory( session=self.session, loader=self.test_loader ) # Fake in the class. self.session.cache.set_resource('test', 'Pipeline', FakePipeResource) def test_init(self): self.assertEqual(self.cf.session, self.session) self.assertTrue(isinstance(self.cf.loader, ResourceJSONLoader)) self.assertEqual(self.cf.base_collection_class, Collection) self.assertEqual(self.cf.details_class, CollectionDetails) # Test overrides (invalid for actual usage). import kotocore cf = CollectionFactory( loader=False, base_collection_class=PipeCollection, details_class=True ) self.assertEqual(cf.session, kotocore.session) self.assertEqual(cf.loader, False) self.assertEqual(cf.base_collection_class, PipeCollection) self.assertEqual(cf.details_class, True) def test_build_class_name(self): self.assertEqual( self.cf._build_class_name('PipelineCollection'), 'PipelineCollection' ) self.assertEqual( self.cf._build_class_name('TestName'), 'TestName' ) def test_build_methods(self): attrs = self.cf._build_methods(self.cd) self.assertEqual(len(attrs), 3) self.assertTrue('create' in attrs) self.assertTrue('each' in attrs) self.assertTrue('test_role' in attrs) def test_create_operation_method(self): class StubbyCollection(Collection): pass class StubbyResource(Resource): _details = ResourceDetails( self.session, 'test', 'Pipeline', loader=self.test_loader ) op_method = self.cf._create_operation_method('create', { "api_name": "CreatePipeline" }) self.assertEqual(op_method.__name__, 'create') self.assertEqual(op_method.__doc__, DEFAULT_DOCSTRING) # Assign it & call it. StubbyCollection._details = self.cd StubbyCollection.create = op_method StubbyCollection.change_resource(StubbyResource) sr = StubbyCollection(connection=FakeConn()) fake_pipe = sr.create() self.assertEqual(fake_pipe.id, '1872baf45') self.assertEqual(fake_pipe.title, 'A pipe') # Make sure an exception is raised when the underlying connection # doesn't have an analogous method. sr = StubbyCollection(connection=OopsConn()) with self.assertRaises(NoSuchMethod): fake_pipe = sr.create() def test_construct_for(self): col_class = self.cf.construct_for('test', 'PipelineCollection')