{ "info": { "author": "Sune Debel", "author_email": "sd@dybro-debel.dk", "bugtrack_url": null, "classifiers": [], "description": "# Build Status\n\nBuild status: \n\n[![CircleCI](https://circleci.com/gh/suned/serum.svg?style=svg)](https://circleci.com/gh/suned/serum)\n\nCode quality:\n\n[![Test Coverage](https://api.codeclimate.com/v1/badges/523bc990f4ef696aa22d/test_coverage)](https://codeclimate.com/github/suned/serum/test_coverage) \n\n[![Maintainability](https://api.codeclimate.com/v1/badges/523bc990f4ef696aa22d/maintainability)](https://codeclimate.com/github/suned/serum/maintainability)\n\n\n\n\n# Description\n`serum` is a fresh take on Dependency Injection in Python 3.\n\n`serum` is pure python and has no dependencies.\n# Installation\n```\n> pip install serum\n```\n# Quickstart\n```python\nfrom serum import inject, dependency, Context\n\n\n# Classes decorated with 'dependency' are injectable types.\n@dependency \nclass Log:\n def info(self, message: str):\n raise NotImplementedError()\n\n\nclass SimpleLog(Log):\n def info(self, message: str):\n print(message)\n\n\nclass StubLog(SimpleLog):\n def info(self, message: str):\n pass\n\n\n@inject # Dependencies are injected using a class decorator...\nclass NeedsLog:\n log: Log # ...and class level annotations...\n\n\nclass NeedsSimpleLog:\n @inject # ...or using a function decorator\n def __init__(self, log: SimpleLog):\n self.log = log \n\n\n@inject\nclass NeedsNamedDependency:\n named_dependency: str # class level annotations annotated with a type that is not\n # decorated with 'dependency' will be treated as a named\n # dependency\n\n\n# Contexts provide dependencies\nwith Context(SimpleLog, named_dependency='this name is injected!'):\n assert isinstance(NeedsLog().log, SimpleLog)\n assert NeedsNamedDependency().named_dependency == 'this name is injected!'\n\n\n# Contexts will always provide the most specific \n# subtype of the requested type. This allows you to change which\n# dependencies are injected.\nwith Context(StubLog):\n NeedsLog().log.info('Hello serum!') # doesn't output anything\n NeedsSimpleLog().log.info('Hello serum!') # doesn't output anything\n```\n# Documentation\n- [`inject`](#inject)\n- [`dependency`](#dependency)\n- [`Context`](#context)\n- [`singleton`](#singleton)\n- [`mock`](#mock)\n- [`match`](#match)\n- [IPython Integration](#ipython-integration)\n\n## `inject`\n`inject` is used to decorate functions and classes in which you want to inject\ndependencies.\n```python\nfrom serum import inject, dependency\n\n@dependency\nclass MyDependency:\n pass\n\n@inject\ndef f(dependency: MyDependency):\n assert isinstance(dependency, MyDependency)\n\nf()\n```\nFunctions decorated with `inject` can be called as normal functions. `serum` will\nnot attempt to inject arguments given at call time.\n```python\n@inject\ndef f(dependency: MyDependency):\n print(dependency)\n\nf('Overridden dependency') # outputs: Overridden dependency \n```\n`inject` will instantiate classes decorated with [`dependency`](#dependency). In\nthis way, your entire dependency graph can be specified using just `inject` and \n`dependency`.\n\nInstances of simple types and objects you want to instantiate yourself can be\ninjected using keyword arguments to [`Context`](#context).\n```python\n@inject\ndef f(dependency: str):\n assert dependency == 'a named dependency'\n\nwith Context(dependency='a named dependency'):\n f()\n```\n`inject` can also be used to decorate classes. \n```python\n@inject\nclass SomeClass:\n dependency: MyDependency \n```\nThis is roughly equivalent to:\n```python\nclass SomeClass:\n @inject\n def __init__(self, dependency: MyDependency):\n self.__dependency = dependency\n\n @property\n def dependency(self) -> MyDependency:\n return self.__dependency\n```\nDependencies that are specified as class level annotations can be overridden\nusing key-word arguments to `__init__`\n```python\nassert SomeClass(dependency='Overridden!').dependency == 'Overridden!'\n```\n## `dependency`\nClasses decorated with `dependency` can be instantiated and injected\nby `serum`.\n```python\nfrom serum import dependency, inject\n\n@dependency\nclass Log:\n def info(self, message):\n print(message)\n\n\n@inject\nclass NeedsLog:\n log: Log\n\n\nassert isinstance(NeedsLog().log, Log)\n```\n`serum` relies on being able to inject all dependencies for `dependency` decorated classes \nrecursively. To achieve this, `serum` assumes that the `__init__` method \nof `dependency` decorated classes can be called without any arguments.\nThis means that all arguments to `__init__` of `dependency` decorated classes must be injected using `inject`.\n```python\n@dependency\nclass SomeDependency:\n def method(self):\n pass\n\n\n@inject\n@dependency\nclass ValidDependency: # OK!\n some_dependency: SomeDependency\n\n def __init__(self):\n ...\n\n\n@dependency\nclass AlsoValidDependency: # Also OK!\n @inject\n def __init__(self, some_dependency: SomeDependency):\n ...\n\n\n@dependency\nclass InvalidDependency:\n def __init__(self, a):\n ...\n\n@inject\ndef f(dependency: InvalidDependency):\n ...\n\nf() \n# raises:\n# TypeError: __init__() missing 1 required positional argument: 'a'\n\n# The above exception was the direct cause of the following exception:\n\n# InjectionError Traceback (most recent call last)\n# ...\n# InjectionError: Could not instantiate dependency \n# when injecting argument \"dependency\" in .\n```\n\nNote that circular dependencies preventing instantiation of `dependency` decorated\nclasses leads to an error.\n```python\n@dependency\nclass AbstractA:\n pass\n\n@dependency\nclass AbstractB:\n pass\n\n\nclass A(AbstractA):\n\n @inject\n def __init__(self, b: AbstractB):\n self.b = b\n\nclass B(AbstractB):\n @inject\n def __init__(self, a: AbstractA):\n self.a = a\n\n@inject\nclass Dependent:\n a: AbstractA\n\n\nwith Context(A, B):\n Dependent().a # raises: CircularDependency: Circular dependency encountered while injecting in \n```\n## `Context`\n`Context`s provide implementations of dependencies. A `Context` will always provide the most\nspecific subtype of the requested type (in Method Resolution Order).\n```python\n@dependency\nclass Super:\n pass\n\n\nclass Sub(Super):\n pass\n\n@inject\nclass NeedsSuper:\n instance: Super\n\n\nwith Context(Sub):\n assert isinstance(NeedsSuper().instance, Sub)\n```\nIt is an error to inject a type in an `Context` that provides two or more equally specific subtypes of that type:\n```python\nclass AlsoSub(Super):\n pass\n\n\nwith Context(Sub, AlsoSub):\n NeedsSuper() # raises: AmbiguousDependencies: Attempt to inject type with equally specific provided subtypes: , \n```\n`Context`s can also be used as decorators:\n```python\ncontext = Context(Sub)\n\n@context\ndef f():\n assert isinstance(NeedsSuper().instance, Sub)\n\n``` \nYou can provide named dependencies of any type using keyword arguments.\n```python\n@inject\nclass Database:\n connection_string: str\n\n\nconnection_string = 'mysql+pymysql://root:my_pass@127.0.0.1:3333/my_db'\ncontext = Context(\n connection_string=connection_string\n)\nwith context:\n assert Database().connection_string == connection_string\n```\n`Context`s are local to each thread. This means that when using multi-threading\neach thread runs in its own context\n```python\nimport threading\n\n\n@singleton\nclass SomeSingleton:\n pass\n\ndef worker_without_environment():\n NeedsSuper().instance\n\n@inject\ndef worker(instance: SomeSingleton):\n print(instance)\n\nwith Context():\n worker() # outputs: \n threading.Thread(target=worker).start() # outputs: \n```\n\n## `singleton`\nTo always inject the same instance of a dependency in the same `Context`, annotate your type with `singleton`.\n```python\nfrom serum import singleton\n\n\n@singleton\nclass ExpensiveObject:\n pass\n\n\n@inject\nclass NeedsExpensiveObject:\n expensive_instance: ExpensiveObject\n\n\ninstance1 = NeedsExpensiveObject()\ninstance2 = NeedsExpensiveObject()\nassert instance1.expensive_instance is instance2.expensive_instance\n```\nNote that `Singleton` dependencies injected in different environments \nwill not refer to the same instance.\n```python\n\nwith Context():\n instance1 = NeedsExpensiveObject()\n\nwith Context():\n assert instance1.expensive_instance is not NeedsExpensiveObject().expensive_instance\n```\n## `mock`\n`serum` has support for injecting `MagicMock`s from the builtin\n`unittest.mock` library in unittests using the `mock` utility\nfunction. Mocks are reset\nwhen the environment context is closed.\n```python\nfrom serum import mock\n\n@dependency\nclass SomeDependency:\n def method(self):\n return 'some value' \n\n@inject\nclass Dependent:\n dependency: SomeDependency\n\n\ncontext = Context()\nwith context:\n mock_dependency = mock(SomeDependency)\n mock_dependency.method.return_value = 'some mocked value'\n instance = Dependent()\n assert instance.dependency is mock_dependency\n assert instance.dependency.method() == 'some mocked value'\n\nwith context:\n instance = Dependent()\n assert instance.dependency is not mock_dependency\n assert isinstance(instance.dependency, SomeDependency)\n```\n`mock` uses its argument to spec the injected instance of `MagicMock`. This means\nthat attempting to call methods that are not defined by the mocked `Component`\nleads to an error\n```python\nwith context:\n mock_dependency = mock(SomeDependency)\n mock_dependency.no_method() # raises: AttributeError: Mock object has no attribute 'no method'\n```\nNote that `mock` will only mock requests of the\nexact type supplied as its argument, but not requests of\nmore or less specific types\n```python\nfrom unittest.mock import MagicMock\n\n@dependency\nclass Super:\n pass\n\n\nclass Sub(Super):\n pass\n\n\nclass SubSub(Sub):\n pass\n\n\n@inject\nclass NeedsSuper:\n injected: Super\n\n\n@inject\nclass NeedsSub:\n injected: Sub\n\n\n@inject\nclass NeedsSubSub:\n injected: SubSub\n\n\nwith Context():\n mock(Sub)\n needs_super = NeedsSuper()\n needs_sub = NeedsSub()\n needs_subsub = NeedsSubSub()\n assert isinstance(needs_super.injected, Super)\n assert isinstance(needs_sub.injected, MagicMock)\n assert isinstance(needs_subsub.injected, SubSub)\n```\n## `match`\n`match` is small utility function for matching `Context` instances\nwith values of an environment variable.\n```python\n# my_script.py\nfrom serum import match, dependency, Context, inject\n\n@dependency\nclass BaseDependency:\n def method(self):\n raise NotImplementedError()\n\n\nclass ProductionDependency(BaseDependency):\n def method(self):\n print('Production!')\n\n\nclass TestDependency(BaseDependency):\n def method(self):\n print('Test!')\n\n\n@inject\ndef f(dependency: BaseDependency):\n dependency.method()\n\n\ncontext = match(\n environment_variable='MY_SCRIPT_ENV', \n default=Context(ProductionDependency),\n PROD=Context(ProductionDependency),\n TEST=Context(TestDependency)\n)\n\nwith context:\n f()\n```\n```\n> python my_script.py\nProduction!\n```\n```\n> MY_SCRIPT_ENV=PROD python my_script.py\nProduction!\n```\n```\n> MY_SCRIPT_ENV=TEST python my_script.py\nTest!\n```\n## IPython Integration\nIt can be slightly annoying to import some `Context` and start it as a\ncontext manager in the beginning of every IPython session. \nMoreover, you quite often want to run an IPython REPL in a special context,\ne.g to provide configuration that is normally supplied through command line\narguments in some other way.\n\nTo this end `serum` can act as an IPython extension. To activate it,\nadd the following lines to your `ipython_config.py`:\n```python\nc.InteractiveShellApp.extensions = ['serum']\n```\nFinally, create a file named `ipython_context.py` in the root of your project. In it,\nassign the `Context` instance you would like automatically started to a global\nvariable named `context`:\n```python\n# ipython_context.py\nfrom serum import Context\n\n\ncontext = Context()\n```\nIPython will now enter this context automatically in the beginning of\nevery REPL session started in the root of your project.\n# Why?\nIf you've been researching Dependency Injection frameworks for python,\nyou've no doubt come across this opinion:\n\n>You dont need Dependency Injection in python. \n>You can just use duck typing and monkey patching!\n\nThe position behind this statement is often that you only need Dependency \nInjection in statically typed languages.\n\nIn truth, you don't really _need_ Dependency Injection in any language, \nstatically typed or otherwise. \nWhen building large applications that need to run in multiple environments however,\nDependency Injection can make your life a lot easier. In my experience,\nexcessive use of monkey patching for managing environments leads to a jumbled\nmess of implicit initialisation steps and `if value is None` type code.\n\nIn addition to being a framework, I've attempted to design `serum` to encourage\ndesigning classes that follow the Dependency Inversion Principle:\n\n> one should \u201cdepend upon abstractions, _not_ concretions.\"\n\nThis is achieved by letting inheritance being the principle way of providing\ndependencies and allowing dependencies to be abstract.\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/suned/serum", "keywords": "dependency-injection,solid,inversion-of-control", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "serum", "package_url": "https://pypi.org/project/serum/", "platform": "", "project_url": "https://pypi.org/project/serum/", "project_urls": { "Homepage": "https://github.com/suned/serum" }, "release_url": "https://pypi.org/project/serum/5.1.0/", "requires_dist": null, "requires_python": ">=3.6", "summary": "Dependency Injection library for Python 3", "version": "5.1.0" }, "last_serial": 4248675, "releases": { "0.2.2": [ { "comment_text": "", "digests": { "md5": "b0916df8abe745c63ea6a84e74b60ef6", "sha256": "7267fa04a6354af956c9cb7de137fce8ca372990e5837b85f6fecdbe5d648586" }, "downloads": -1, "filename": "serum-0.2.2-py3-none-any.whl", "has_sig": false, "md5_digest": "b0916df8abe745c63ea6a84e74b60ef6", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 5566, "upload_time": "2018-01-20T00:37:07", "url": "https://files.pythonhosted.org/packages/c6/2b/3db4e433803f4469ff958a2fb1f507b7e7700192a4811141df97785924f9/serum-0.2.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "ac2227feec3a3154ec6f9cbddb9de64c", "sha256": "23b868c84ae440e24e08717f20c9fb658f89a47dda1f9574ab591cc97d6bd288" }, "downloads": -1, "filename": "serum-0.2.2.tar.gz", "has_sig": false, "md5_digest": "ac2227feec3a3154ec6f9cbddb9de64c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4773, "upload_time": "2018-01-20T00:37:08", "url": "https://files.pythonhosted.org/packages/59/b2/abb3710c1dd55d92f00b55ab4790b024a749328cf99f2651cf47aa6baa2d/serum-0.2.2.tar.gz" } ], "0.2.3": [ { "comment_text": "", "digests": { "md5": "2003df898666582268f49d4778f007d8", "sha256": "cc601e43078abd3cbd308ad7de851ac4a12de249b50688faa04feed474b7ae48" }, "downloads": -1, "filename": "serum-0.2.3-py3-none-any.whl", "has_sig": false, "md5_digest": "2003df898666582268f49d4778f007d8", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 5575, "upload_time": "2018-01-20T00:44:01", "url": "https://files.pythonhosted.org/packages/05/a3/de48c84a41c0b19870166bbde1b5fa62f3edc6af27ceea3887f7e3ba31c1/serum-0.2.3-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "27fa9e60eac4c892faa46fa36bf0ba3e", "sha256": "55c3d6f78c6dc7aeb7da94bd8d1b47e186b71312fd5dc98e3fb98bff75b34dff" }, "downloads": -1, "filename": "serum-0.2.3.tar.gz", "has_sig": false, "md5_digest": "27fa9e60eac4c892faa46fa36bf0ba3e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4809, "upload_time": "2018-01-20T00:44:02", "url": "https://files.pythonhosted.org/packages/37/4e/18ec9f28934ccf7d1c15bdab9a5b9576825cbc231bcc720b9fd8ff7eae10/serum-0.2.3.tar.gz" } ], "1.0.0": [ { "comment_text": "", "digests": { "md5": "b42ee63440a4983a08273edaac4f6002", "sha256": "7a73b736f13946990635219a82213a2b035863b746c1b4ce21ef4d295fa04717" }, "downloads": -1, "filename": "serum-1.0.0-py3-none-any.whl", "has_sig": false, "md5_digest": "b42ee63440a4983a08273edaac4f6002", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 5657, "upload_time": "2018-01-20T10:28:19", "url": "https://files.pythonhosted.org/packages/79/de/160d6998f08eee51271abdc741538ea8c635c206f4f6225d5553ad7272af/serum-1.0.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "8abc8b410e01447b3987d23600bf6b65", "sha256": "a696b0e51281074e198cb4bd7908699de761ab104da03cdac8efd400766d53a4" }, "downloads": -1, "filename": "serum-1.0.0.tar.gz", "has_sig": false, "md5_digest": "8abc8b410e01447b3987d23600bf6b65", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4864, "upload_time": "2018-01-20T10:28:20", "url": "https://files.pythonhosted.org/packages/8b/0f/679c6df685e8c0f8b100c0b08f80c078c5defd02885d061941cc97f10889/serum-1.0.0.tar.gz" } ], "1.1.0": [ { "comment_text": "", "digests": { "md5": "83ebb06aea342e0d3b43153216f17842", "sha256": "3924145718da50be978d75976c458cce7a6c3f1a0b08616cd74e737e967c14f2" }, "downloads": -1, "filename": "serum-1.1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "83ebb06aea342e0d3b43153216f17842", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 6025, "upload_time": "2018-01-23T18:47:34", "url": "https://files.pythonhosted.org/packages/2b/85/832dbfb0777704b70ea029dd290196e1db694eddb6b477082040a46be727/serum-1.1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "587152f80704fee102512f5cc9bc4d21", "sha256": "f2b3e62515e6a6bd9340a45e3b13aa4c7c63bac928b288ed7caf04fc91957b55" }, "downloads": -1, "filename": "serum-1.1.0.tar.gz", "has_sig": false, "md5_digest": "587152f80704fee102512f5cc9bc4d21", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 5436, "upload_time": "2018-01-23T18:47:35", "url": "https://files.pythonhosted.org/packages/1f/a8/e06d72bc03fa42d04b6208b547ada51c6e36a3285ca7dceb0d81a70cf643/serum-1.1.0.tar.gz" } ], "1.1.1": [ { "comment_text": "", "digests": { "md5": "abdbfcdffae8b512edfe2fe82cb1006e", "sha256": "0f3c45bcbcac16a6c620bf03ffdee14ae91348475032fef58551ddde260709bd" }, "downloads": -1, "filename": "serum-1.1.1-py3-none-any.whl", "has_sig": false, "md5_digest": "abdbfcdffae8b512edfe2fe82cb1006e", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 6445, "upload_time": "2018-02-01T21:21:28", "url": "https://files.pythonhosted.org/packages/13/d3/837e812bb82a256c135812eea855d05d9a9e95e9b02760461f83b24b0a32/serum-1.1.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "e669c47160f2f722d485426f2a0d5d01", "sha256": "9b250b1e0a4650e0100d0fe11fd6e6868b0aebad733428a3d2a2bc0cda2b0600" }, "downloads": -1, "filename": "serum-1.1.1.tar.gz", "has_sig": false, "md5_digest": "e669c47160f2f722d485426f2a0d5d01", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 5985, "upload_time": "2018-02-01T21:21:29", "url": "https://files.pythonhosted.org/packages/21/10/03097986f292f1ee1c704a6b1e27b20d259b7d6d7d943f11b41c5bed4056/serum-1.1.1.tar.gz" } ], "1.1.3": [ { "comment_text": "", "digests": { "md5": "f50a4e2767975a5086fd1eb05529a7ce", "sha256": "8dcdd3c32cb2e4410cd40210a25b29f3fb74fd4c25eaaf903726a8c67d9e63b2" }, "downloads": -1, "filename": "serum-1.1.3-py3-none-any.whl", "has_sig": false, "md5_digest": "f50a4e2767975a5086fd1eb05529a7ce", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 6525, "upload_time": "2018-02-02T15:00:01", "url": "https://files.pythonhosted.org/packages/b1/95/9049de9eadea0835102f7e23d7bb40481834eb2aa56252f67ec21fddb098/serum-1.1.3-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "38077884e20a8c96306aa12a452655c9", "sha256": "728b7007d0ade7fd4fd0de5aab17dbb8939dc43db52102f3d548cdc772094307" }, "downloads": -1, "filename": "serum-1.1.3.tar.gz", "has_sig": false, "md5_digest": "38077884e20a8c96306aa12a452655c9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6183, "upload_time": "2018-02-02T15:00:04", "url": "https://files.pythonhosted.org/packages/0f/2a/8e474552095955fe0468a325a36a93520bef17952f69a676e1f94a8a42d8/serum-1.1.3.tar.gz" } ], "1.1.4": [ { "comment_text": "", "digests": { "md5": "47e1742d4846b422d2a5aff96c7ee878", "sha256": "f23fa7c039aeff2705649593a63b26aa9e9d9cc1562a9203ae2b874312b40271" }, "downloads": -1, "filename": "serum-1.1.4-py3-none-any.whl", "has_sig": false, "md5_digest": "47e1742d4846b422d2a5aff96c7ee878", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 6468, "upload_time": "2018-02-03T15:25:57", "url": "https://files.pythonhosted.org/packages/1c/be/7d5b92695283f73faa608a2b738c13975a0a2da86e371dc31b758af2fc3c/serum-1.1.4-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "753eac3052527819a4918b177cbb61da", "sha256": "25b8aeb3d71dcbb3f51937d4b5a525a7756e5711b68c841c3d5c90250dc9f9d8" }, "downloads": -1, "filename": "serum-1.1.4.tar.gz", "has_sig": false, "md5_digest": "753eac3052527819a4918b177cbb61da", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6078, "upload_time": "2018-02-03T15:25:59", "url": "https://files.pythonhosted.org/packages/45/65/61ceddd3055906d159b13ae26f2ce9804bf61a15df4de0619e939db71411/serum-1.1.4.tar.gz" } ], "1.1.5": [ { "comment_text": "", "digests": { "md5": "a9036935eff2c700c8ce02cd280850e5", "sha256": "e51a17b783b451a36b84e26bbbc3f055501c5274d1ec3cfc52b581c9ea6977e6" }, "downloads": -1, "filename": "serum-1.1.5-py3-none-any.whl", "has_sig": false, "md5_digest": "a9036935eff2c700c8ce02cd280850e5", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 6374, "upload_time": "2018-02-07T22:24:23", "url": "https://files.pythonhosted.org/packages/94/59/d16984a056ce1f6582d6e9e61f1c2a53e29cdcbbda80d192c0f567cc5c32/serum-1.1.5-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "5b739b2b2725ba80d157c5d0013603ab", "sha256": "e09d078ff3d59604228c6fbc331fcc8015fbc4e8e42b5b1faad9dea2c03cd044" }, "downloads": -1, "filename": "serum-1.1.5.tar.gz", "has_sig": false, "md5_digest": "5b739b2b2725ba80d157c5d0013603ab", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6102, "upload_time": "2018-02-07T22:24:25", "url": "https://files.pythonhosted.org/packages/f3/f7/8a38c2c4462c53cd8dc1a0897b1c7ca52ed469e47563c171b3cabf00c2c3/serum-1.1.5.tar.gz" } ], "1.2.0": [ { "comment_text": "", "digests": { "md5": "fdf02112438d7793afb395cee09cb2c1", "sha256": "ad16bcec7f8a45286aa30a41f731499594940e968fcd8405a4afbe113a7b7f09" }, "downloads": -1, "filename": "serum-1.2.0-py3-none-any.whl", "has_sig": false, "md5_digest": "fdf02112438d7793afb395cee09cb2c1", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 6456, "upload_time": "2018-02-14T11:49:16", "url": "https://files.pythonhosted.org/packages/ce/e3/df455bc0fb253dd80ad623fc5a727f1dc5afa2556daa55fd9281d9ba05cb/serum-1.2.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "34a4052fe69506b80e1790bd302524e8", "sha256": "8622c201de66c5581cd495ebcb2425c8bb5b2555a96fb8a3ac729211be8cb3ee" }, "downloads": -1, "filename": "serum-1.2.0.tar.gz", "has_sig": false, "md5_digest": "34a4052fe69506b80e1790bd302524e8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6461, "upload_time": "2018-02-14T11:49:17", "url": "https://files.pythonhosted.org/packages/13/e6/769d84c068b77ffdcbb8067fb92538c5f20f658e51751885f7d8fd64ef5a/serum-1.2.0.tar.gz" } ], "2.0.0": [ { "comment_text": "", "digests": { "md5": "f41567c240d01dd9d0ef59219b947f11", "sha256": "0e69e0d5a3dfb0bbb8313b051c1b3048f1e43ff199fa9e0237688b0806e1103f" }, "downloads": -1, "filename": "serum-2.0.0-py3-none-any.whl", "has_sig": false, "md5_digest": "f41567c240d01dd9d0ef59219b947f11", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 6776, "upload_time": "2018-02-21T20:29:35", "url": "https://files.pythonhosted.org/packages/02/a4/4c036636413e80f214b6634b300319d094c66c7cca2dcf1163e7ea88f8e3/serum-2.0.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "008f59ac7f764e15dcdf085c176ff2c7", "sha256": "df591ea3d8767a98f59ebfafaff1bc11a5964966efa8a51ed318a0e157431f68" }, "downloads": -1, "filename": "serum-2.0.0.tar.gz", "has_sig": false, "md5_digest": "008f59ac7f764e15dcdf085c176ff2c7", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7075, "upload_time": "2018-02-21T20:29:36", "url": "https://files.pythonhosted.org/packages/89/a5/5168fa18c5217149ebf5e99361db1380c416ec2080d8c1349c22f8d322dc/serum-2.0.0.tar.gz" } ], "3.0.1": [ { "comment_text": "", "digests": { "md5": "c857e0b005fcc915de670c8f746d9c6e", "sha256": "7e900dd5f2ce915e0225e80c80db564f2e640e1f5b828d36966c94e2eabc8ab5" }, "downloads": -1, "filename": "serum-3.0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "c857e0b005fcc915de670c8f746d9c6e", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 7655, "upload_time": "2018-03-11T20:54:20", "url": "https://files.pythonhosted.org/packages/cf/67/93d917d5b711017cd296fe0075c0efb949a9989077fbe053680bb68c06e3/serum-3.0.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "1393cb82f5f1f95ea018008e930e222c", "sha256": "c24637350d89f9b46c8f755da51948ff5622d7307f39141296d993278530b9f6" }, "downloads": -1, "filename": "serum-3.0.1.tar.gz", "has_sig": false, "md5_digest": "1393cb82f5f1f95ea018008e930e222c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7973, "upload_time": "2018-03-11T20:54:21", "url": "https://files.pythonhosted.org/packages/fd/13/d6138fa554d842e05cfa37d4b851501cccb547b2f87f1338b6b4cff07aba/serum-3.0.1.tar.gz" } ], "4.0.0": [ { "comment_text": "", "digests": { "md5": "81c34dcfe6cde990c641c916b03179ac", "sha256": "67c37234ac5e66bf20f0c97259af5d13efac99d1e57ea1f3da48f3af97a54f12" }, "downloads": -1, "filename": "serum-4.0.0-py3-none-any.whl", "has_sig": false, "md5_digest": "81c34dcfe6cde990c641c916b03179ac", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 12312, "upload_time": "2018-04-18T13:40:11", "url": "https://files.pythonhosted.org/packages/90/9d/d4424fa77d5f036a63a9682a1115639da8fd2e75cf4f3f990767dfebd5cf/serum-4.0.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "badf99187d900b251df6e64a5073b281", "sha256": "86b2a4730fb3484958ff7a9060d63a2e8d7662b1b7d34e9ee70393b028fa1f51" }, "downloads": -1, "filename": "serum-4.0.0.tar.gz", "has_sig": false, "md5_digest": "badf99187d900b251df6e64a5073b281", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16896, "upload_time": "2018-04-18T13:40:12", "url": "https://files.pythonhosted.org/packages/be/72/c9de24b5edd8d8fe338f1ed4f95f6370a2a885588c993d854d759c898c1a/serum-4.0.0.tar.gz" } ], "4.0.1": [ { "comment_text": "", "digests": { "md5": "ee2d1380cae8368d996dbf6fe14da4e2", "sha256": "f9d6a1feeff9f70b8ff48ff652d093a05fe0ebe9de4d7644d4e418bcc896e28e" }, "downloads": -1, "filename": "serum-4.0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "ee2d1380cae8368d996dbf6fe14da4e2", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.5", "size": 12082, "upload_time": "2018-04-18T14:35:30", "url": "https://files.pythonhosted.org/packages/3e/9f/d0ba61cb137a0ac0d3facddb38b2d095989ba2b7631043c9a485d9d57211/serum-4.0.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "de42cdcf7e9af819f27d3e03c940e2fc", "sha256": "c90a47ea999d3aa57e71431d40e6b32a2ec48704132df8b73a267515d7f1e2fa" }, "downloads": -1, "filename": "serum-4.0.1.tar.gz", "has_sig": false, "md5_digest": "de42cdcf7e9af819f27d3e03c940e2fc", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.5", "size": 20488, "upload_time": "2018-04-18T14:35:32", "url": "https://files.pythonhosted.org/packages/e5/88/7ab9e0d01f422e171fcf30af5f9d64a9ccb59e3210d7673054d76d6d0cb9/serum-4.0.1.tar.gz" } ], "4.0.2": [ { "comment_text": "", "digests": { "md5": "6bc6d7f422dc8f9a7b5898493efab103", "sha256": "9620c765b826781e32b43e37bae6f66a21e4cb94856c90d7f41b1189de6a6a7d" }, "downloads": -1, "filename": "serum-4.0.2-py3-none-any.whl", "has_sig": false, "md5_digest": "6bc6d7f422dc8f9a7b5898493efab103", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.5", "size": 12116, "upload_time": "2018-04-22T19:56:54", "url": "https://files.pythonhosted.org/packages/af/f6/87a08060afcc3b121d212344f90576f353f02b0c48749303691c0def5c19/serum-4.0.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "9a4342fca3688e296e3a6071c5c4837a", "sha256": "5a670938eb55f9f8fa76b903a009026e71b96d97a820935fa686012a4b3e2b8d" }, "downloads": -1, "filename": "serum-4.0.2.tar.gz", "has_sig": false, "md5_digest": "9a4342fca3688e296e3a6071c5c4837a", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.5", "size": 17868, "upload_time": "2018-04-22T19:56:55", "url": "https://files.pythonhosted.org/packages/ab/06/af2b2866c3f57041cd1c384749d4d10b79a18e7033e9bdc1baff2e13a644/serum-4.0.2.tar.gz" } ], "5.0.0": [ { "comment_text": "", "digests": { "md5": "110977c839d0bc6fdd9e83022b66eb32", "sha256": "eaeac11238569a8f0d69ed9e2559ba87f130e1a5def45214f05e2984ce1037ed" }, "downloads": -1, "filename": "serum-5.0.0-py3-none-any.whl", "has_sig": false, "md5_digest": "110977c839d0bc6fdd9e83022b66eb32", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6", "size": 12076, "upload_time": "2018-04-24T14:59:23", "url": "https://files.pythonhosted.org/packages/dd/0b/95522cc68e4d124261e93e8a014cd2feefa468e353586da13a92cdeac5fc/serum-5.0.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "784bc5787e737707b3099e117bc7bb28", "sha256": "c75e62fb9c0238dabf8caff74d4b6fdad0d89e09598cf76d1d3b24a7c2e0cbf1" }, "downloads": -1, "filename": "serum-5.0.0.tar.gz", "has_sig": false, "md5_digest": "784bc5787e737707b3099e117bc7bb28", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 15520, "upload_time": "2018-04-24T14:59:24", "url": "https://files.pythonhosted.org/packages/17/8b/d277e44d508e0c2f33885f9762b44dbf9b29845c0e9e36ee1367285899bf/serum-5.0.0.tar.gz" } ], "5.1.0": [ { "comment_text": "", "digests": { "md5": "b251ea71f5500aea6beab90d0f266b46", "sha256": "165566380d55ae9f58e76e0dcf4882d89a7130ef681d2d6ea0f8c4be0c6c77af" }, "downloads": -1, "filename": "serum-5.1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "b251ea71f5500aea6beab90d0f266b46", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6", "size": 12074, "upload_time": "2018-09-07T14:35:43", "url": "https://files.pythonhosted.org/packages/26/bf/0e02d4fe8b3438c7d3300648b3c64909df03daffc4d07348cf93e74a39f8/serum-5.1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "abdb1938dcfdde09188b745c6f66a18f", "sha256": "e3a05656583a6fce914f51e7741ce773d6e97f4516485d456e40a00afb8e2251" }, "downloads": -1, "filename": "serum-5.1.0.tar.gz", "has_sig": false, "md5_digest": "abdb1938dcfdde09188b745c6f66a18f", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 15373, "upload_time": "2018-09-07T14:35:43", "url": "https://files.pythonhosted.org/packages/5f/c8/8f43f98bf795ec7de87c98b22c68860319ea2357573de12a2f550d794f11/serum-5.1.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "b251ea71f5500aea6beab90d0f266b46", "sha256": "165566380d55ae9f58e76e0dcf4882d89a7130ef681d2d6ea0f8c4be0c6c77af" }, "downloads": -1, "filename": "serum-5.1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "b251ea71f5500aea6beab90d0f266b46", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6", "size": 12074, "upload_time": "2018-09-07T14:35:43", "url": "https://files.pythonhosted.org/packages/26/bf/0e02d4fe8b3438c7d3300648b3c64909df03daffc4d07348cf93e74a39f8/serum-5.1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "abdb1938dcfdde09188b745c6f66a18f", "sha256": "e3a05656583a6fce914f51e7741ce773d6e97f4516485d456e40a00afb8e2251" }, "downloads": -1, "filename": "serum-5.1.0.tar.gz", "has_sig": false, "md5_digest": "abdb1938dcfdde09188b745c6f66a18f", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 15373, "upload_time": "2018-09-07T14:35:43", "url": "https://files.pythonhosted.org/packages/5f/c8/8f43f98bf795ec7de87c98b22c68860319ea2357573de12a2f550d794f11/serum-5.1.0.tar.gz" } ] }