diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..36257ac --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.py[cod] +*.egg-info +*.egg +*.so +dist/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..52ef3eb --- /dev/null +++ b/.travis.yml @@ -0,0 +1,22 @@ +language: python + +python: + - 2.6 + - 2.7 + - pypy + - 3.2 + - 3.3 + - 3.4 + +install: + - pip install -r requirements.txt + - pip install -r requirements-dev.txt + +script: + - coverage run --source=pydic setup.py test + +after_success: + - coveralls + +notifications: + email: false \ No newline at end of file diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..6a77e44 --- /dev/null +++ b/README.rst @@ -0,0 +1,196 @@ +Container +========= + +Parameters +---------- +The ``pydic.parameters.Parameters`` class is a simple container for key/value pairs. + +The available methods are: + +- ``set(key, value)``: Sets a parameter. +- ``get(key, default=None)``: Returns a parameter by name. If the key don't exists, the default parameter will be returned. +- ``has(key)``: Returns *True* if the parameter exists, *False* otherwise. +- ``remove(key)``: Removes a parameter. +- ``add(parameters)``: Adds a dict of parameters +- ``all()``: Returns all set parameters. +- ``count()``: Returns the number of all set parameters. +- ``keys()``: Returns the all set parameter keys. +- ``parse_text(text)``: Resolves a string which can contain parameters (example: 'Hello {{ name }} {{ surname }}!') + + +.. note:: + + You can reference others parameters wrapping it between ``{{`` ``}}`` characters. + + For example: ``'foo': '{{ bar }}', 'bar': 'aaa'``, if you get the ``foo`` parameter, the return value should be ``aaa`` because ``foo -> {{ bar }} -> bar -> aaa`` + + You can escape brackets processing with "``\``". + + For example, if you set a parameter with the following value ``Hello \{\{ name \}\}``, if you get it, the return will be ``Hello {{ name }}!`` + + +Services +-------- + +What is a Service Container +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +A Service Container (or *dependency injection container*) is simply a python object that manages the instantiation of services (objects). +For example, suppose you have a simple python class that delivers email messages. Without a service container, you must manually create the object whenever you need it: + +.. code-block:: python + + from myapplication.mailer import Mailer + + mailer = Mailer('sendmail') + mailer.send('felix@example.com', ...) + +This is easy enough. The imaginary *Mailer* class allows you to configure the method used to deliver the +email messages (e.g. *sendmail*, *smtp*, etc). + +But what if you wanted to use the mailer service somewhere else? You certainly don't want to repeat the mailer +configuration every time you need to use the Mailer object. What if you needed to change the *transport* from *sendmail* +to *smtp* everywhere in the application? You'd need to hunt down every place you create a *Mailer* service and change it. + +The Services container allows you to standardize and centralize the way objects are constructed in your application. + +Creating/Configuring Services in the Container +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A better answer is to let the service container create the *Mailer* object for you. +In order for this to work, you must teach the container how to create the *Mailer* service. +This is done via configuration definitions: + +.. code-block:: python + + ... + definitions = { + 'my_mailer': { + 'class': 'myapplication.mailer.Mailer', + 'arguments': ['sendmail'] + } + } + + services = Services(definitions) + ... + + +When you ask for the *my_mailer* service from the container ``services.get('my_mailer')``, the container constructs the object and returns it. + +This is another major advantage of using the service container. Namely, a service is never constructed until it's needed. +If you define a service and never use it, the service is never created. This saves memory and increases +the speed of your application. This also means that there's very little or no performance hit for defining lots +of services. **Services that are never used are never constructed.** + +As an added bonus, the *Mailer* service is only created once and the same instance is returned each time you ask for +the service. This is almost always the behavior you'll need (it's more flexible and powerful). + +You can pass the arguments as list or dict. + +Also you can call functions after object instantiation with: + +.. code-block:: python + + ... + definitions = { + 'my_mailer': { + 'class': 'myapplication.mailer.Mailer', + 'arguments': ['sendmail'], + 'calls': [ + [ 'set_name', 'Felix Carmona'], + [ 'inject_something', [1, 2, 3]], + [ 'inject_something', [2, 3]], + [ 'set_location', {'city': 'Barcelona', 'country': 'Spain'}] + ] + } + } + ... + + +Once the container has been constructed with the definitions, the available methods for the service container object are: + +- ``set(key, value)``: Sets a service object by name. +- ``get(key)``: Returns a service object by name. +- ``has(key)``: Returns *True* if the service definition exists or if the service object is instantiated, *False* otherwise. +- ``remove(key)``: Removes a service object and service definition by name. +- ``add(parameters)``: Adds a dict of services objects. +- ``keys()``: Returns the services keys. + + +Using the Parameters to build Services +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The creation of new services (objects) via the container is pretty straightforward. Parameters make defining services +more organized and flexible: + +.. code-block:: python + + ... + parameters = Parameters( + { + 'my_mailer_class': 'myapplication.mailer.Mailer', + 'my_mailer_transport': 'sendmail' + } + ) + + definitions = { + 'my_mailer': { + 'class': '{{ my_mailer_class }}', + 'arguments': ['{{ my_mailer_transport }}'] + } + } + + services = Services(definitions, parameters) + ... + + +The end result is exactly the same as before - the difference is only in how you defined the service. +By surrounding the *my_mailer.class* and *my_mailer.transport* strings in double bracket keys (``{{`` ``}}``) signs, the services container knows to look +for parameters with those names. Parameters can deep reference other parameters that references other parameters, and will +be resolved anyway. + +The purpose of parameters is to feed information into services. Of course there was nothing wrong with defining the +service without using any parameters. Parameters, however, have several advantages: + + - separation and organization of all service "options" under a single parameters key + - parameter values can be used in multiple service definitions + +The choice of using or not using parameters is up to you. + + +Referencing (Injecting) Services +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can of course also reference services + +Start the string with @ to reference a service, example: + +.. code-block:: python + + ... + parameters = Parameters( + { + 'my_mailer_class': 'myapplication.mailer.Mailer', + 'my_mailer_transport': 'sendmail' + } + ) + + definitions = { + 'my_mailer': { + 'class': '{{ my_mailer_class }}', + 'arguments': ['{{ my_mailer_transport }}'] + }, + 'my_mailer_manager': {} + 'class': 'myapplication.mailer.MailerManager', + 'arguments': ['@my_mailer'] + } + } + + services = Services(definitions, parameters) + ... + + +the *my_mailer* service will be injected in the *my_mailer_manager* + +.. note:: + + Use ``@@`` to escape the ``@`` symbol. ``@@my_mailer`` will be converted into the string "``@my_mailer``" instead of referencing the + *my_mailer* service. \ No newline at end of file diff --git a/pydic/__init__.py b/pydic/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pydic/parameters.py b/pydic/parameters.py new file mode 100644 index 0000000..acf2905 --- /dev/null +++ b/pydic/parameters.py @@ -0,0 +1,58 @@ +from jinja2 import Template + + +class Parameters: + def __init__(self, parameters=None): + """ @type parameters: dict """ + self._parameters = parameters if parameters else {} + + def set(self, key, value): + self._parameters[key] = value + + def parse_text(self, text): + # python 2-3 compatibility + try: + text_type = (str, unicode) + except NameError: # pragma: no cover + text_type = str + + if not isinstance(text, text_type): + return text + template = Template(text) + resolved_text = template.render(self._parameters) + if resolved_text == text: + resolved_text = resolved_text.replace("\\{", "{") + resolved_text = resolved_text.replace("\\}", "}") + return resolved_text + else: + return self.parse_text(resolved_text) + + def get(self, key, default=None): + """ @type key: str """ + if key not in self._parameters: + return default + + value = self._parameters[key] + + return self.parse_text(value) + + def has(self, key): + """ @type key: str """ + return key in self._parameters + + def remove(self, key): + """ @type key: str """ + del self._parameters[key] + + def add(self, parameters): + """ @type parameters: dict """ + self._parameters = dict(list(self._parameters.items()) + list(parameters.items())) + + def all(self): + return self._parameters + + def count(self): + return len(self._parameters) + + def keys(self): + return list(self._parameters.keys()) diff --git a/pydic/services.py b/pydic/services.py new file mode 100644 index 0000000..72d61d2 --- /dev/null +++ b/pydic/services.py @@ -0,0 +1,142 @@ +import sys +from pydic.parameters import Parameters + +definitions = { + 'my_mailer': { + 'class': 'myapplication.mailer.Mailer', + 'arguments': ['sendmail'] + } +} + +class Services: + def __init__(self, definitions=None, parameters=None): + """ + @type definitions: dict + @type parameters: Parameters + """ + self._services = {} + self._definitions = definitions if definitions else {} + self._parameters = parameters if parameters else Parameters() + + def set(self, key, value): + self._services[key] = value + + def get(self, name): + if name in self._services: + return self._services[name] + + if name not in self._definitions: + raise ServicesException('Service \'%s\' not found.' % name) + + return self._build_service(name) + + def has(self, key): + """ @type key: str """ + return key in self._definitions or key in self._services + + def remove(self, key): + """ @type key: str """ + if key in self._services: + del self._services[key] + if key in self._definitions: + del self._definitions[key] + + def add(self, services): + """ @type services: dict """ + self._services = dict(list(self._services.items()) + list(services.items())) + + def keys(self): + definitions_keys = self._definitions.keys() + services_keys = self._services.keys() + return set(list(definitions_keys) + list(services_keys)) + + def _build_service(self, definition_name): + definition = self._definitions[definition_name] + if isinstance(definition, str): + return self._resolve_container_key(definition) + if 'class' not in definition: + raise ServicesException('\'%s\' definition has no attribute \'class\'' % definition_name) + + module_name, class_name = definition['class'].rsplit('.', 1) + __import__(module_name) + module = sys.modules[module_name] + service_class = getattr(module, class_name) + if 'arguments' in definition: + arguments = definition['arguments'] + if isinstance(arguments, dict): + # python 2-3 compatibility + try: + arguments_iteritems = arguments.iteritems() + except AttributeError: # pragma: no cover + arguments_iteritems = arguments.items() + + for argument_key, argument_value in arguments_iteritems: + arguments[argument_key] = self._resolve_container_key(argument_value) + service_object = service_class(**arguments) + elif isinstance(arguments, list): + resolved_arguments = [] + for argument in arguments: + resolved_arguments.append(self._resolve_container_key(argument)) + service_object = service_class(*resolved_arguments) + else: + raise ServicesException('Invalid arguments type \'%s\', for \'%s\' definition, use dict or tuple.' % (type(definition['arguments']), definition_name)) + else: + service_object = service_class() + + if 'calls' in definition: + for call in definition['calls']: + if isinstance(call, str): + resolved_argument = self._resolve_container_key(call) + self._validate_call_in_service(service_object, resolved_argument, definition_name) + getattr(service_object, call)() + elif isinstance(call[1], list): + resolved_arguments = [] + for argument in call[1]: + resolved_arguments.append(self._resolve_container_key(argument)) + self._validate_call_in_service(service_object, call[0], definition_name) + getattr(service_object, call[0], lambda: None)(*resolved_arguments) + elif isinstance(call[1], dict): + resolved_arguments = {} + + # python 2-3 compatibility + try: + call_iteritems = call[1].iteritems() + except AttributeError: # pragma: no cover + call_iteritems = call[1].items() + + for argument_key, argument_value in call_iteritems: + resolved_arguments[argument_key] = self._resolve_container_key(argument_value) + self._validate_call_in_service(service_object, call[0], definition_name) + getattr(service_object, call[0], lambda: None)(**resolved_arguments) + else: + raise ServicesException('Invalid call type \'%s\', for \'%s\' definition, use str (for no args), or list or dict (for args)' % (type(call), definition_name)) + + self._services[definition_name] = service_object + return self._services[definition_name] + + @staticmethod + def _validate_call_in_service(service_object, function_name, definition_name): + if not hasattr(service_object, function_name): + raise ServicesException('Invalid call function \'%s\' for \'%s\' definition' % (function_name, definition_name)) + + def _resolve_container_key(self, key): + key = self._parameters.parse_text(key) + + # python 2-3 compatibility + try: + text_type = (str, unicode) + except NameError: # pragma: no cover + text_type = str + + if isinstance(key, text_type) and len(key) >= 2 and key[0] == '@': + key = key[1:] + if key[0] == '@': + return key + else: + return self.get(key) + else: + return key + + +class ServicesException(Exception): + pass \ No newline at end of file diff --git a/pydic/tests/__init__.py b/pydic/tests/__init__.py new file mode 100644 index 0000000..80eec88 --- /dev/null +++ b/pydic/tests/__init__.py @@ -0,0 +1,8 @@ +import os.path +from discover import DiscoveringTestLoader + + +def get_tests(): # pragma: no cover + start_dir = os.path.dirname(__file__) + test_loader = DiscoveringTestLoader() + return test_loader.discover(start_dir, pattern="test_*.py") \ No newline at end of file diff --git a/pydic/tests/test_parameters.py b/pydic/tests/test_parameters.py new file mode 100644 index 0000000..a6aa853 --- /dev/null +++ b/pydic/tests/test_parameters.py @@ -0,0 +1,52 @@ +from unittest import TestCase +from pydic.parameters import Parameters + + +class ParametersTestCase(TestCase): + def test_get(self): + parameters = Parameters({'foo': 'bar', 'hello': 'world'}) + self.assertEqual('bar', parameters.get('foo')) + self.assertEqual('world', parameters.get('hello')) + self.assertEqual('bbb', parameters.get('aaa', 'bbb')) + + def test_get_parameter_which_references_other_parameter(self): + parameters = Parameters({'name': 'Felix', 'hello_message': 'Hi {{ name }}!'}) + self.assertEqual('Hi Felix!', parameters.get('hello_message')) + + def test_set(self): + parameters = Parameters({'foo': 'bar', 'hello': 'world'}) + parameters.set('foo', 'abc') + self.assertTrue('abc', parameters.get('foo')) + + def test_has(self): + parameters = Parameters({'foo': 'bar', 'hello': 'world'}) + self.assertTrue(parameters.has('foo')) + self.assertFalse(parameters.has('another')) + self.assertTrue(parameters.has('hello')) + + def test_all(self): + params = {'foo': 'bar', 'hello': 'world'} + parameters = Parameters(params) + self.assertEqual(parameters.all(), params) + + def test_add(self): + parameters = Parameters({'foo': 'bar'}) + parameters.add({'hello': 'world', 'bbb': 222}) + self.assertEqual('bar', parameters.get('foo')) + self.assertEqual('world', parameters.get('hello')) + self.assertEqual(222, parameters.get('bbb')) + + def test_remove(self): + parameters = Parameters({'foo': 'bar', 'hello': 'world', 'aaa': 111}) + parameters.remove('hello') + self.assertEqual({'foo': 'bar', 'aaa': 111}, parameters.all()) + + def test_keys(self): + parameters = Parameters({'foo': 'bar', 'hello': 'world'}) + keys = parameters.keys() + self.assertTrue('foo' in keys) + self.assertTrue('hello' in keys) + + def test_count(self): + parameters = Parameters({'foo': 'bar', 'hello': 'world', 'aaa': 111}) + self.assertEqual(3, parameters.count()) diff --git a/pydic/tests/test_services.py b/pydic/tests/test_services.py new file mode 100644 index 0000000..c85281c --- /dev/null +++ b/pydic/tests/test_services.py @@ -0,0 +1,339 @@ +from unittest import TestCase +from pydic.services import Services, ServicesException +from pydic.parameters import Parameters + + +class SimpleService: + def __init__(self): + pass + + def say(self): + return 'hello' + + +class SimpleServiceWithConstructorArguments: + def __init__(self, name, surname): + self._name = name + self._surname = surname + + def say(self): + return 'hello %s %s' % (self._name, self._surname) + + +class SimpleServiceWithCallsWithArguments: + def __init__(self): + self._name = None + self._surname = None + + def set_name(self, name): + self._name = name + + def set_surname(self, surname): + self._surname = surname + + def say(self): + return 'hello %s %s' % (self._name, self._surname) + + +class SimpleServiceWithCallWithArguments: + def __init__(self): + self._name = None + self._surname = None + + def set_name_surname(self, name, surname): + self._name = name + self._surname = surname + + def say(self): + return 'hello %s %s' % (self._name, self._surname) + + +class SimpleServiceWithCallsWithoutArguments: + def __init__(self): + self._name = None + self._surname = None + + def set_name(self): + self._name = 'Felix' + + def set_surname(self): + self._surname = 'Carmona' + + def say(self): + return 'hello %s %s' % (self._name, self._surname) + + +class CarService: + def __init__(self, driver): + self._driver = driver + + def drive(self): + return '%s is driving' % self._driver.get_name() + + +class DriverService: + def __init__(self, name): + self._name = name + + def get_name(self): + return self._name + + +class ServicesTestCase(TestCase): + def test_get_most_simple_service(self): + definitions = { + 'simple': { + 'class': 'pydic.tests.test_services.SimpleService' + } + } + services = Services(definitions) + service = services.get('simple') + self.assertEqual('hello', service.say()) + same_service = services.get('simple') + self.assertTrue(service is same_service) + + def test_fail_when_tries_to_get_an_unknown_service(self): + services = Services({}) + self.assertRaises(ServicesException, services.get, 'unknown_service') + + def test_service_definition_referencing_other_service_definition(self): + definitions = { + 'simple': { + 'class': 'pydic.tests.test_services.SimpleService' + }, + 'alias_of_service': '@simple' + } + services = Services(definitions) + service = services.get('alias_of_service') + self.assertEqual('hello', service.say()) + same_service = services.get('simple') + self.assertTrue(service is same_service) + + def test_escape_service(self): + definitions = { + 'simple': { + 'class': 'pydic.tests.test_services.SimpleServiceWithConstructorArguments', + 'arguments': { + 'name': '@@foo', + 'surname': 'abc' + } + } + } + + services = Services(definitions) + service = services.get('simple') + self.assertEqual('hello @foo abc', service.say()) + + def test_escape_parameter(self): + definitions = { + 'simple': { + 'class': 'pydic.tests.test_services.SimpleServiceWithConstructorArguments', + 'arguments': { + 'name': "\{\{ foo \}\} {{ surname }}", + 'surname': 'abc' + } + } + } + parameters = { + 'surname': 'Carmona' + } + + services = Services(definitions, Parameters(parameters)) + service = services.get('simple') + self.assertEqual('hello {{ foo }} Carmona abc', service.say()) + + def test_service_definition_with_parameter_argument(self): + definitions = { + 'simple': { + 'class': 'pydic.tests.test_services.SimpleServiceWithConstructorArguments', + 'arguments': { + 'name': '{{ my_user_name }}', + 'surname': '{{ my_user_surname }}xxx' + } + } + } + + parameters = Parameters({'my_user_name': 'Felix', 'my_user_surname': 'Carmona'}) + services = Services(definitions, parameters) + service = services.get('simple') + self.assertEqual('hello Felix Carmonaxxx', service.say()) + + def test_fail_when_tries_to_get_a_malformed_definition(self): + definitions = { + 'simple': { + 'xxx': 'aaa' + } + } + services = Services(definitions) + self.assertRaises(ServicesException, services.get, 'simple') + + def test_service_with_constructor_arguments_as_dict(self): + definitions = { + 'simple': { + 'class': 'pydic.tests.test_services.SimpleServiceWithConstructorArguments', + 'arguments': { + 'name': 'Felix', + 'surname': 'Carmona' + } + } + } + services = Services(definitions) + service = services.get('simple') + self.assertEqual('hello Felix Carmona', service.say()) + + def test_service_with_constructor_arguments_as_list(self): + definitions = { + 'simple': { + 'class': 'pydic.tests.test_services.SimpleServiceWithConstructorArguments', + 'arguments': ['Felix', 'Carmona'] + } + } + services = Services(definitions) + service = services.get('simple') + self.assertEqual('hello Felix Carmona', service.say()) + + def test_fail_when_definition_arguments_are_not_dict_or_tuple_or_list(self): + definitions = { + 'simple': { + 'class': 'pydic.tests.test_services.SimpleServiceWithConstructorArguments', + 'arguments': 'Felix' + } + } + services = Services(definitions) + self.assertRaises(ServicesException, services.get, 'simple') + + def test_service_with_calls_with_arguments_as_list(self): + definitions = { + 'simple': { + 'class': 'pydic.tests.test_services.SimpleServiceWithCallsWithArguments', + 'calls': [ + ['set_name', ['Felix']], + ['set_surname', ['Carmona']] + ] + } + } + services = Services(definitions) + service = services.get('simple') + self.assertEqual('hello Felix Carmona', service.say()) + + def test_service_with_calls_with_arguments_as_dict(self): + definitions = { + 'simple': { + 'class': 'pydic.tests.test_services.SimpleServiceWithCallWithArguments', + 'calls': [ + ['set_name_surname', {'surname': 'Carmona', 'name': 'Felix'}] + ] + } + } + services = Services(definitions) + service = services.get('simple') + self.assertEqual('hello Felix Carmona', service.say()) + + def test_service_with_calls_without_arguments(self): + definitions = { + 'simple': { + 'class': 'pydic.tests.test_services.SimpleServiceWithCallsWithoutArguments', + 'calls': [ + 'set_name', + 'set_surname' + ] + } + } + services = Services(definitions) + service = services.get('simple') + self.assertEqual('hello Felix Carmona', service.say()) + + def test_service_with_sub_dependency(self): + definitions = { + 'car': { + 'class': 'pydic.tests.test_services.CarService', + 'arguments': ['@driver'] + }, + 'driver': { + 'class': 'pydic.tests.test_services.DriverService', + 'arguments': ['{{ driver_name }}'] + } + } + parameters = Parameters({'driver_name': 'Felix'}) + services = Services(definitions, parameters) + service = services.get('car') + self.assertEqual('Felix is driving', service.drive()) + + def test_fail_when_call_function_arguments_are_malformed(self): + definitions = { + 'simple': { + 'class': 'pydic.tests.test_services.SimpleServiceWithCallWithArguments', + 'calls': [ + ['set_name_surname', 1] + ] + } + } + services = Services(definitions) + self.assertRaises(ServicesException, services.get, 'simple') + + def test_fail_when_call_function_not_exists_in_service(self): + definitions = { + 'simple': { + 'class': 'pydic.tests.test_services.SimpleServiceWithCallsWithoutArguments', + 'calls': [ + 'set_namex' + ] + } + } + services = Services(definitions) + self.assertRaises(ServicesException, services.get, 'simple') + + def test_set_service(self): + simple = SimpleService() + services = Services() + services.set('simple', simple) + same_simple = services.get('simple') + self.assertEqual('hello', same_simple.say()) + + def test_has_service(self): + definitions = { + 'simple': { + 'class': 'pydic.tests.test_services.SimpleService' + } + } + services = Services(definitions) + self.assertTrue(services.has('simple')) + self.assertFalse(services.has('foo_service')) + + def test_remove_service(self): + definitions = { + 'simple': { + 'class': 'pydic.tests.test_services.SimpleService' + } + } + services = Services(definitions) + services.get('simple') + services.remove('simple') + self.assertFalse(services.has('simple')) + + def test_add_services(self): + definitions = { + 'simple': { + 'class': 'pydic.tests.test_services.SimpleService' + } + } + services = Services(definitions) + services.add({'new_service_one': SimpleService(), 'new_service_two': SimpleService()}) + self.assertTrue(services.has('simple')) + self.assertTrue(services.has('new_service_one')) + self.assertTrue(services.has('new_service_two')) + + def test_get_keys_services(self): + definitions = { + 'simple': { + 'class': 'pydic.tests.test_services.SimpleService' + }, + 'other': { + 'class': 'pydic.tests.test_services.SimpleService' + } + } + services = Services(definitions) + services.add({'new_service_one': SimpleService(), 'new_service_two': SimpleService()}) + expected = ['other', 'simple', 'new_service_one', 'new_service_two'] + actual = services.keys() + self.assertEqual(set(expected), set(actual)) \ No newline at end of file diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..1a62a17 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,3 @@ +discover +coverage +coveralls \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e2b0716 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +jinja2==2.5 \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..e247915 --- /dev/null +++ b/setup.py @@ -0,0 +1,23 @@ +import os +from setuptools import setup, find_packages + + +requirements_file = open('%s/requirements.txt' % os.path.dirname(os.path.realpath(__file__)), 'r') +install_requires = [line.rstrip() for line in requirements_file] +base_dir = os.path.dirname(os.path.abspath(__file__)) + +setup( + name="pydic", + version="0.0.1", + description="Manage your services via a robust and flexible Dependency Injection Container", + long_description="\n\n".join([ + open(os.path.join(base_dir, "README.rst"), "r").read(), + ]), + url="https://github.com/felixcarmona/pydic", + author="Felix Carmona", + author_email="mail@felixcarmona.com", + packages=find_packages(exclude=('pydic.tests', 'apy.tests.*')), + zip_safe=False, + install_requires=install_requires, + test_suite="pydic.tests.get_tests", +) \ No newline at end of file