{ "info": { "author": "Oleg Pidsadnyi, Paylogic International and others", "author_email": "developers@paylogic.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 6 - Mature", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: MacOS :: MacOS X", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Testing", "Topic :: Utilities" ], "description": "halogen\n=======\n\nPython HAL generation/parsing library.\n\n.. image:: http://img.shields.io/pypi/v/halogen.svg\n :target: https://pypi.python.org/pypi/halogen\n.. image:: http://img.shields.io/coveralls/paylogic/halogen/master.svg\n :target: https://coveralls.io/r/paylogic/halogen\n.. image:: https://travis-ci.org/paylogic/halogen.svg?branch=master\n :target: https://travis-ci.org/paylogic/halogen\n.. image:: https://readthedocs.org/projects/halogen/badge/?version=latest\n :alt: Documentation Status\n :scale: 100%\n :target: https://readthedocs.org/projects/halogen/\n\nHalogen takes the advantage of the declarative style serialization with easily extendable schemas.\nSchema combines the knowledge about your data model, attribute mapping and advanced accessing, with\ncomplex types and data transformation.\n\nLibrary is purposed in representing your data in HAL format in the most obvious way possible, but also\nof the generic web form-like functionality so that your schemas and types can be reused as much as possible.\n\n\nSchema\n======\n\nSchema is the main building block of the serialization. It is also a type which means you can declare nested\nstructures with schemas.\n\n\nSerialization\n-------------\n\n.. code-block:: python\n\n >>> Schema.serialize({\"hello\": \"Hello World\"})\n >>> {\"hello\": \"Hello World\"}\n\nSimply call Schema.serialize() class method which can accept dict or any other object.\n\n\nValidation\n----------\n\nThere's no validation involved in the serialization. Your source data or your model is considered\nto be clean since it is coming from the storage and it is not a user input. Of course exceptions\nin the types or attribute accessors may occur but they are considered as programming errors.\n\n\nSerializing dict\n----------------\n\nDictionary values are automatically accessed by the schema attributes using their names as keys:\n\n.. code-block:: python\n\n import halogen\n\n class Hello(halogen.Schema):\n hello = halogen.Attr()\n\n\n serialized = Hello.serialize({\"hello\": \"Hello World\"})\n\n\nResult:\n\n.. code-block:: json\n\n {\n \"hello\": \"Hello World\"\n }\n\nHAL is just JSON, but according to it's specification it SHOULD have self link to identify the\nserialized resource. For this you should use HAL-specific attributes and configure the way the\n``self`` is composed.\n\n\nHAL example:\n\n.. code-block:: python\n\n import halogen\n from flask import url_for\n\n spell = {\n \"uid\": \"abracadabra\",\n \"name\": \"Abra Cadabra\",\n \"cost\": 10,\n }\n\n class Spell(halogen.Schema):\n\n self = halogen.Link(attr=lambda spell: url_for(\"spell.get\" uid=spell['uid']))\n name = halogen.Attr()\n\n serialized = Spell.serialize(spell)\n\nResult:\n\n.. code-block:: json\n\n {\n \"_links\": {\n \"self\": {\"href\": \"/spells/abracadabra\"}\n },\n \"name\": \"Abra Cadabra\"\n }\n\n\nSerializing objects\n-------------------\n\nSimilar to dictionary keys the schema attributes can also access object properties:\n\n.. code-block:: python\n\n import halogen\n from flask import url_for\n\n class Spell(object):\n uid = \"abracadabra\"\n name = \"Abra Cadabra\"\n cost = 10\n\n spell = Spell()\n\n class SpellSchema(halogen.Schema):\n self = halogen.Link(attr=lambda spell: url_for(\"spell.get\" uid=spell.uid))\n name = halogen.Attr()\n\n serialized = SpellSchema.serialize(spell)\n\nResult:\n\n.. code-block:: json\n\n {\n \"_links\": {\n \"self\": {\"href\": \"/spells/abracadabra\"}\n },\n \"name\": \"Abra Cadabra\"\n }\n\n\nAttribute\n---------\n\nAttributes form the schema and encapsulate the knowledge how to get the data from your model,\nhow to transform it according to the specific type.\n\n\nAttr()\n~~~~~~\n\nThe name of the attribute member in the schema is the name of the key the result will be serialized to.\nBy default the same attribute name is used to access the source model.\n\nExample:\n\n.. code-block:: python\n\n import halogen\n from flask import url_for\n\n class Spell(object):\n uid = \"abracadabra\"\n name = \"Abra Cadabra\"\n cost = 10\n\n spell = Spell()\n\n class SpellSchema(halogen.Schema):\n self = halogen.Link(attr=lambda spell: url_for(\"spell.get\" uid=spell.uid))\n name = halogen.Attr()\n\n serialized = SpellSchema.serialize(spell)\n\nResult:\n\n.. code-block:: json\n\n {\n \"_links\": {\n \"self\": {\"href\": \"/spells/abracadabra\"}\n },\n \"name\": \"Abra Cadabra\"\n }\n\n\nAttr(\"const\")\n~~~~~~~~~~~~~\n\nIn case the attribute represents a constant the value can be specified as a first parameter. This first parameter\nis a type of the attribute. If the type is not a instance or subclass of a ``halogen.types.Type`` it will\nbe bypassed.\n\n.. code-block:: python\n\n import halogen\n from flask import url_for\n\n class Spell(object):\n uid = \"abracadabra\"\n name = \"Abra Cadabra\"\n cost = 10\n\n spell = Spell()\n\n class SpellSchema(halogen.Schema):\n self = halogen.Link(attr=lambda spell: url_for(\"spell.get\" uid=spell.uid))\n name = halogen.Attr(\"custom name\")\n\n serialized = SpellSchema.serialize(spell)\n\nResult:\n\n.. code-block:: json\n\n {\n \"_links\": {\n \"self\": {\"href\": \"/spells/abracadabra\"}\n },\n \"name\": \"custom name\"\n }\n\nIn some cases also the ``attr`` can be specified to be a callable that returns a constant value.\n\n\nAttr(attr=\"foo\")\n~~~~~~~~~~~~~~~~\n\nIn case the attribute name doesn't correspond your model you can override it:\n\n.. code-block:: python\n\n import halogen\n from flask import url_for\n\n class Spell(object):\n uid = \"abracadabra\"\n title = \"Abra Cadabra\"\n cost = 10\n\n spell = Spell()\n\n class SpellSchema(halogen.Schema):\n self = halogen.Link(attr=lambda spell: url_for(\"spell.get\" uid=spell.uid))\n name = halogen.Attr(attr=\"title\")\n\n serialized = SpellSchema.serialize(spell)\n\nResult:\n\n.. code-block:: json\n\n {\n \"_links\": {\n \"self\": {\"href\": \"/spells/abracadabra\"}\n },\n \"name\": \"Abra Cadabra\"\n }\n\nThe ``attr`` parameter accepts strings of the source attribute name or even dot-separated path to the attribute.\nThis works for both: nested dictionaries or related objects an Python properties.\n\n\n.. code-block:: python\n\n import halogen\n\n class SpellSchema(halogen.Schema):\n name = halogen.Attr(attr=\"path.to.my.attribute\")\n\n\nAttr(attr=lambda value: value)\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe ``attr`` parameter accepts callables that take the entire source model and can access the neccessary\nattribute. You can pass a function or lambda in order to return the desired value which\nalso can be just a constant.\n\n.. code-block:: python\n\n import halogen\n from flask import url_for\n\n class Spell(object):\n uid = \"abracadabra\"\n title = \"Abra Cadabra\"\n cost = 10\n\n spell = Spell()\n\n class SpellSchema(halogen.Schema):\n self = halogen.Link(attr=lambda spell: url_for(\"spell.get\" uid=spell.uid))\n name = halogen.Attr(attr=lambda value: value.title)\n\n serialized = SpellSchema.serialize(spell)\n\nResult:\n\n.. code-block:: json\n\n {\n \"_links\": {\n \"self\": {\"href\": \"/spells/abracadabra\"}\n },\n \"name\": \"Abra Cadabra\"\n }\n\n\nAttribute as a decorator\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nSometimes accessor functions are too big for lambdas. In this case it is possible\nto decorate a method of the class to be a getter accessor.\n\n\n.. code-block:: python\n\n import halogen\n\n class ShoppingCartSchema(halogen.Schema):\n\n @halogen.attr(AmountType(), default=None)\n def total(obj):\n return sum(\n (item.amount for item in obj.items),\n 0,\n )\n\n @total.setter\n def set_total(obj, value):\n obj.total = value\n\n\n\nAttr(attr=Acccessor)\n~~~~~~~~~~~~~~~~~~~~\n\nIn case the schema is used for both directions to serialize and to deserialize the ``halogen.schema.Accessor``\ncan be passed with both ``getter`` and ``setter`` specified.\n``Getter`` is a string or callable in order to get the value from a model, and ``setter`` is a string or callable\nthat knows where the deserialized value should be stored.\n\n\nAttr(Type())\n~~~~~~~~~~~~\n\nAfter the attibute gets the value it passes it to it's type in order to complete the serialization.\nHalogen provides basic types for example ``halogen.types.List`` to implement lists of values or schemas.\nSchema is also a Type and can be passed to the attribute to implement complex structures.\n\nExample:\n\n.. code-block:: python\n\n import halogen\n from flask import url_for\n\n class Book(object):\n uid = \"good-book-uid\"\n title = \"Harry Potter and the Philosopher's Stone\"\n genres = [\n {\"uid\": \"fantasy-literature\", \"title\": \"fantasy literature\"},\n {\"uid\": \"mystery\", \"title\": \"mystery\"},\n {\"uid\": \"adventure\", \"title\": \"adventure\"},\n ]\n\n book = Book()\n\n class GenreSchema(halogen.Schema):\n self = halogen.Link(attr=lambda genre: url_for(\"genre.get\" uid=genre['uid']))\n title = halogen.Attr()\n\n class BookSchema(halogen.Schema):\n self = halogen.Link(attr=lambda book: url_for(\"book.get\" uid=book.uid))\n title = halogen.Attr()\n genres = halogen.Attr(halogen.types.List(GenreSchema))\n\n serialized = BookSchema.serialize(book)\n\nResult:\n\n.. code-block:: json\n\n {\n \"_links\": {\n \"self\": {\"href\": \"good-book-uid\"}\n },\n \"genres\": [\n {\"_links\": {\"self\": {\"href\": \"fantasy-literature\"}}, \"title\": \"fantasy literature\"},\n {\"_links\": {\"self\": {\"href\": \"mystery\"}}, \"title\": \"mystery\"},\n {\"_links\": {\"self\": {\"href\": \"adventure\"}}, \"title\": \"adventure\"}\n ],\n \"title\": \"Harry Potter and the Philosopher's Stone\"\n }\n\nAttr(Type(validators=[validator]))\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nType gets optional ``validators`` parameter, which is a list of ``halogen.validators.Validator`` objects whose single\ninterface method ``validate`` will be called for the given value during the deserialization. If the value is not valid,\n``halogen.exceptions.ValidationError`` should be raised.\nHalogen provides basic validators, for example ``halogen.validators.Range`` to validate that the values is in certain\nrange.\n\n\nAttr(default=value)\n~~~~~~~~~~~~~~~~~~~\n\nIf an attribute cannot be taken, provided ``default`` value will be used; if ``default`` value is\na callable, it will be called to get the default value.\n\n\nAttr(required=False)\n~~~~~~~~~~~~~~~~~~~~\n\nBy default, attributes are required, so when an attribute can not be taken during the serialization and ``default``\nis not provided, an exception will be raised (``AttributeError`` or ``KeyError``, depending on the input).\nIt's possible to relax this restriction by passing ``required=False`` to the attribute constructor.\nFor deserialization, the same logic applies, but the exception type will be ``halogen.exceptions.ValidationError``\nfor human readability (see Deserialization_).\n\n\nType\n----\n\nType is responsible in serialization of individual values such as integers, strings, dates. Also type\nis a base of Schema. It has both serialize() and deserialize() methods that convert the attribute's value.\nUnlike Schema types are instantiated. You can configure serialization behavior by passing parameters to\ntheir constructors while declaring your schema.\n\nTypes can raise ``halogen.exceptions.ValidationError`` during deserialization, but serialization\nexpects the value that this type knows how to transform.\n\n\nSubclassing types\n~~~~~~~~~~~~~~~~~\n\nTypes that are common in your application can be shared between schemas. This could be the datetime type,\nspecific URL type, internationalized strings and any other representation that requires specific format.\n\n\nType.serialize\n~~~~~~~~~~~~~~\n\nThe default implementation of the Type.serialize is a bypass.\n\nSerialization method of a type is the last opportunity to convert the value that is being serialized:\n\nExample:\n\n.. code-block:: python\n\n import halogen\n\n class Amount(object):\n currency = \"EUR\"\n amount = 1\n\n\n class AmountType(halogen.types.Type):\n def serialize(self, value):\n\n if value is None or not isinstance(value, Amount):\n return None\n\n return {\n \"currency\": value.currency,\n \"amount\": value.amount\n }\n\n\n class Product(object):\n name = \"Milk\"\n\n def __init__(self):\n self.price = Amount()\n\n product = Product()\n\n\n class ProductSchema(halogen.Schema):\n\n name = halogen.Attr()\n price = halogen.Attr(AmountType())\n\n serialized = ProductSchema.serialize(product)\n\nResult:\n\n.. code-block:: json\n\n {\n \"name\": \"Milk\",\n \"price\": {\n \"amount\": 1,\n \"currency\": \"EUR\"\n }\n }\n\nNullable types\n~~~~~~~~~~~~~~\n\nIn case the accessor returns None and the further serialization by a type or a nested schema\nis not desired the type can be wrapped into `Nullable` type.\n\n\n.. code-block:: python\n\n import halogen\n\n\n class FreeProduct(object):\n \"\"\"A free product, that doesn't have a price.\"\"\"\n\n price = None\n\n\n class AmountSchema(halogen.Schema):\n\n currency = halogen.Attr(required=True, default=\"USD\")\n amount = halogen.Attr(required=True, default=0)\n\n\n class FreeProductSchema(halogen.Schema):\n\n price_null = halogen.Attr(halogen.types.Nullable(AmountType()), attr=\"price\")\n price_zero = halogen.Attr(AmountType(), attr=\"price\")\n\n\n serialized = FreeProductSchema.serialize(FreeProduct())\n\n\nResult:\n\n.. code-block:: python\n\n {\n \"price_null\": None,\n \"price_zero\": {\n \"amount\": 0,\n \"currency\": \"USD\"\n }\n }\n\n\nHAL\n===\n\nHypertext Application Language.\n\n\nRFC\n---\n\nThe JSON variant of HAL (application/hal+json) has now been published as an internet draft: draft-kelly-json-hal_\n\n.. _draft-kelly-json-hal: http://tools.ietf.org/html/draft-kelly-json-hal.\n\n\nLink\n----\n\nLink objects at RFC: link-objects_\n\n.. _link-objects: http://tools.ietf.org/html/draft-kelly-json-hal-06#section-5\n\n\nhref\n----\n\nThe \"href\" property is REQUIRED.\n\n``halogen.Link`` will create ``href`` for you. You just need to point to ``halogen.Link`` either from where or\nwhat ``halogen.Link`` should put into ``href``.\n\nStatic variant\n .. code-block:: python\n\n import halogen\n\n class EventSchema(halogen.Schema):\n\n artist = halogen.Link(attr=\"/artists/some-artist\")\n\nCallable variant\n .. code-block:: python\n\n import halogen\n\n class EventSchema(halogen.Schema):\n\n help = halogen.Link(attr=lambda: current_app.config['DOC_URL'])\n\ndeprecation\n-----------\n\nLinks can be deprecated by specifying the deprecation URL attribute which points to the document\ndescribing the deprecation.\n\n\n .. code-block:: python\n\n import halogen\n\n class EventSchema(halogen.Schema):\n\n artist = halogen.Link(\n attr=\"/artists/some-artist\",\n deprecation=\"http://docs.api.com/deprecations#artist\",\n )\n\n\nCURIE\n~~~~~\n\nCURIEs are providing links to the resource documentation.\n\n.. code-block:: python\n\n import halogen\n\n doc = halogen.Curie(\n name=\"doc,\n href=\"http://haltalk.herokuapp.com/docs/{rel}\",\n templated=True\n )\n\n class BlogSchema(halogen.Schema):\n\n lastest_post = halogen.Link(attr=\"/posts/latest\", curie=doc)\n\n\n.. code-block:: json\n\n {\n \"_links\": {\n \"curies\": [\n {\n \"name\": \"doc\",\n \"href\": \"http://haltalk.herokuapp.com/docs/{rel}\",\n \"templated\": true\n }\n ],\n\n \"doc:latest_posts\": {\n \"href\": \"/posts/latest\"\n }\n }\n }\n\nSchema also can be a param to link\n\n.. code-block:: python\n\n import halogen\n\n class BookLinkSchema(halogen.Schema):\n href = halogen.Attr(\"/books\")\n\n class BookSchema(halogen.Schema):\n\n books = halogen.Link(BookLinkSchema)\n\n serialized = BookSchema.serialize({\"books\": \"\"})\n\n.. code-block:: python\n\n {\n \"_links\": {\n \"books\": {\n \"href\": \"/books\"\n }\n }\n }\n\n\nEmbedded\n~~~~~~~~\n\nThe reserved \"_embedded\" property is OPTIONAL. It is an object whose property names are link relation types (as\ndefined by [RFC5988]) and values are either a Resource Object or an array of Resource Objects.\n\nEmbedded Resources MAY be a full, partial, or inconsistent version of\nthe representation served from the target URI.\n\nFor creating ``_embedded`` in your schema you should use ``halogen.Embedded``.\n\nExample:\n\n.. code-block:: python\n\n import halogen\n\n em = halogen.Curie(\n name=\"em\",\n href=\"https://docs.event-manager.com/{rel}.html\",\n templated=True,\n type=\"text/html\"\n )\n\n\n class EventSchema(halogen.Schema):\n self = halogen.Link(\"/events/activity-event\")\n collection = halogen.Link(\"/events/activity-event\", curie=em)\n uid = halogen.Attr()\n\n\n class PublicationSchema(halogen.Schema):\n self = halogen.Link(attr=lambda publication: \"/campaigns/activity-campaign/events/activity-event\")\n event = halogen.Link(attr=lambda publication: \"/events/activity-event\", curie=em)\n campaign = halogen.Link(attr=lambda publication: \"/campaign/activity-event\", curie=em)\n\n\n class EventCollection(halogen.Schema):\n self = halogen.Link(\"/events\")\n events = halogen.Embedded(halogen.types.List(EventSchema), attr=lambda collection: collection[\"events\"], curie=em)\n publications = halogen.Embedded(\n attr_type=halogen.types.List(PublicationSchema),\n attr=lambda collection: collection[\"publications\"],\n curie=em\n )\n\n\n collections = {\n 'events': [\n {\"uid\": 'activity-event'}\n ],\n 'publications': [\n {\n \"event\": {\"uid\": \"activity-event\"},\n \"campaign\": {\"uid\": \"activity-campaign\"}\n }\n ]\n }\n\n serialized = EventCollection.serialize(collections)\n\nResult:\n\n.. code-block:: json\n\n {\n \"_embedded\": {\n \"em:events\": [\n {\n \"_links\": {\n \"curies\": [\n {\n \"href\": \"https://docs.event-manager.com/{rel}.html\",\n \"name\": \"em\",\n \"templated\": true,\n \"type\": \"text/html\"\n }\n ],\n \"em:collection\": {\"href\": \"/events/activity-event\"},\n \"self\": {\"href\": \"/events/activity-event\"}\n },\n \"uid\": \"activity-event\"\n }\n ],\n \"em:publications\": [\n {\n \"_links\": {\n \"curies\": [\n {\n \"href\": \"https://docs.event-manager.com/{rel}.html\",\n \"name\": \"em\",\n \"templated\": true,\n \"type\": \"text/html\"\n }\n ],\n \"em:campaign\": {\"href\": \"/campaign/activity-event\"},\n \"em:event\": {\"href\": \"/events/activity-event\"},\n \"self\": {\"href\": \"/campaigns/activity-campaign/events/activity-event\"}\n }\n }\n ]\n },\n \"_links\": {\n \"curies\": [\n {\n \"href\": \"https://docs.event-manager.com/{rel}.html\",\n \"name\": \"em\",\n \"templated\": true,\n \"type\": \"text/html\"\n }\n ],\n \"self\": {\"href\": \"/events\"}\n }\n }\n\nBy default, embedded resources are required, you can make them not required by passing ``required=False`` to the\nconstructor, and empty values will be omitted in the serialization:\n\n.. code-block:: python\n\n import halogen\n\n class Schema(halogen.Schema):\n user1 = halogen.Embedded(PersonSchema, required=False)\n user2 = halogen.Embedded(PersonSchema)\n\n serialized = Schema.serialize({'user2': Person(\"John\", \"Smith\")})\n\nResult:\n\n.. code-block:: json\n\n {\n \"_embedded\": {\n \"user2\": {\n \"name\": \"John\",\n \"surname\": \"Smith\"\n }\n }\n }\n\n\nDeserialization\n===============\n\nSchema has ``deserialize`` method. Method ``deserialize`` will return dict as a result of deserialization\nif you wont pass any object as a second param.\n\nExample:\n\n.. code-block:: python\n\n import halogen\n\n class Hello(halogen.Schema):\n hello = halogen.Attr()\n\n result = Hello.deserialize({\"hello\": \"Hello World\"})\n print(result)\n\nResult:\n\n.. code-block:: python\n\n {\n \"hello\": \"Hello World\"\n }\n\nHowever, if you will pass object as the second param of ``deserialize`` method then data will be assigned on object's\nattributes.\n\nExample:\n\n.. code-block:: python\n\n import halogen\n\n class HellMessage(object):\n hello = \"\"\n\n\n hello_message = HellMessage()\n\n\n class Hello(halogen.Schema):\n hello = halogen.Attr()\n\n\n Hello.deserialize({\"hello\": \"Hello World\"}, hello_message)\n print(hello_message.hello)\n\nResult:\n\n.. code-block:: python\n\n \"Hello World\"\n\n\nType.deserialize\n----------------\n\nHow you already know attributes launch ``serialize`` method from types which they are supported in moment of\nserialization but in case of deserialization the same attributes will launch ``deserialize`` method. It means that\nwhen you write your types you should not forget about ``deserialize`` methods for them.\n\nExample:\n\n.. code-block:: python\n\n import halogen\n import decimal\n\n\n class Amount(object):\n currency = \"EUR\"\n amount = 1\n\n def __init__(self, currency, amount):\n self.currency = currency\n self.amount = amount\n\n def __repr__(self):\n return \"Amount: {currency} {amount}\".format(currency=self.currency, amount=str(self.amount))\n\n\n class AmountType(halogen.types.Type):\n\n def serialize(self, value):\n\n if value is None or not isinstance(value, Amount):\n return None\n\n return {\n \"currency\": value.currency,\n \"amount\": value.amount\n }\n\n def deserialize(self, value):\n return Amount(value[\"currency\"], decimal.Decimal(str(value[\"amount\"])))\n\n\n class ProductSchema(halogen.Schema):\n title = halogen.Attr()\n price = halogen.Attr(AmountType())\n\n\n product = ProductSchema.deserialize({\"title\": \"Pencil\", \"price\": {\"currency\": \"EUR\", \"amount\": 0.30}})\n print(product)\n\n\nResult:\n\n.. code-block:: python\n\n {\"price\": Amount: EUR 0.3, \"title\": \"Pencil\"}\n\n\nDeserialization validation errors\n---------------------------------\n\nOn deserialization failure, halogen raises special exception (``halogen.exceptions.ValidationError``).\nThat exception class has ``__unicode__`` method which renders human readable error result so user can easily track\ndown the problem with his input.\n\n\nExample:\n\n.. code-block:: python\n\n import halogen\n\n class Hello(halogen.Schema):\n hello = halogen.Attr()\n\n try:\n result = Hello.deserialize({})\n except halogen.exceptions.ValidationError as exc:\n print(exc)\n\nResult:\n\n.. code-block:: python\n\n {\n \"errors\": [\n {\n \"errors\": [\n {\n \"type\": \"str\",\n \"error\": \"Missing attribute.\"\n }\n ],\n \"attr\": \"hello\"\n }\n ],\n \"attr\": \"\"\n }\n\n\nIn case when you have nested schemas, and use ``List``, halogen also adds the index (counting from 0) in the list\nso you see where exactly the validation error happened.\n\n\nExample:\n\n.. code-block:: python\n\n import halogen\n\n class Product(halogen.Schema):\n\n \"\"\"A product has a name and quantity.\"\"\"\n\n name = halogen.Attr()\n quantity = halogen.Attr()\n\n\n class NestedSchema(halogen.Schema):\n\n \"\"\"An example nested schema.\"\"\"\n\n products = halogen.Attr(\n halogen.types.List(\n Product,\n ),\n default=[],\n )\n\n try:\n result = NestedSchema.deserialize({\n \"products\": [\n {\n \"name\": \"name\",\n \"quantity\": 1\n },\n {\n \"name\": \"name\",\n }\n\n ]\n })\n except halogen.exceptions.ValidationError as exc:\n print(exc)\n\nResult:\n\n.. code-block:: python\n\n {\n \"errors\": [\n {\n \"errors\": [\n {\n \"index\": 1,\n \"errors\": [\n {\n \"errors\": [\n {\n \"type\": \"str\",\n \"error\": \"Missing attribute.\"\n }\n ],\n \"attr\": \"quantity\"\n }\n ]\n }\n ],\n \"attr\": \"products\"\n }\n ],\n \"attr\": \"\"\n }\n\nNote that should ``ValueError`` exception happen on the attribute deserialization, it will be caught and reraized\nas ``halogen.exceptions.ValidationError``. This is to eliminate the need of raising halogen specific exceptions in\ntypes and attributes during the deserialization.\n\n\nProviding context\n~~~~~~~~~~~~~~~~~\n\nWhen serializing or deserializing an object, not all data required for (de)serialization may be available in the object\nitself. You can pass this data as separate keyword arguments to `serialize` or ``deserialize`` to provide context.\nThis context will be available in all nested schema, types and attributes.\n\nSerialize example:\n\n.. code-block:: python\n\n class ErrorSchema(halogen.Schema):\n message = halogen.Attr(\n attr=lambda error, language: error[\"message\"][language]\n )\n\n error = ErrorSchema.serialize({\n \"message\": {\n \"dut\": \"Ongeldig e-mailadres\",\n \"eng\": \"Invalid email address\"\n }\n }, language=\"dut\")\n\n print error\n\nResult:\n\n.. code-block:: python\n\n {\"message\": \"Ongeldig e-mailadres\"}\n\n\nDeserialize example:\n\n.. code-block:: python\n\n import halogen\n\n\n class Book(halogen.Schema):\n\n @halogen.attr()\n def title(obj, language):\n return obj['title'][language]\n\n class Author(halogen.Schema):\n name = halogen.Attr(attr='author.name')\n books = halogen.Attr(\n halogen.types.List(Book),\n attr='author.books',\n )\n\n author = Author.deserialize({\n \"author\": {\n \"name\": \"Roald Dahl\",\n \"books\": [\n {\n \"title\": {\n \"dut\": \"De Heksen\",\n \"eng\": \"The Witches\"\n }\n },\n {\n \"title\": {\n \"dut\": \"Sjakie en de chocoladefabriek\",\n \"eng\": \"Charlie and the Chocolate Factory\"\n }\n }\n ]\n }\n }, language=\"eng\")\n\n print author\n\nResult:\n\n.. code-block:: python\n\n {\n \"name\": \"Roald Dahl\",\n \"books\": [\n {\"title\": \"The Witches\"},\n {\"title\": \"Charlie and the Chocolate Factory\"}\n ]\n }\n\n\nVendor media types\n------------------\n\nHandling validation and business logic errors are as important as handling HAL responses.\nHalogen provides support for the vendor error media type, which is fully HAL-compatible.\n\nvnd.error\n=========\n\nThe vendor error (application/vnd.error+json) has now been published as an internet draft: draft-vnd-error_\n\n.. _draft-vnd-error: http://nocarrier.co.uk/profiles/vnd.error/\n\nThis mediatype is attempting to standartise the format in which the problem can be represented to many clients\nso that it can be expressed and understood.\nMultiple deserialization errors can be mapped to the relevant keys of the payload via the path attribute, which\nrepresents the JSON Pointer to the payload key, and therefore to the UI element that is serialized with that key.\n\n\n.. code-block:: python\n\n import halogen\n from halogen.vnd.error import Error, VNDError\n\n\n class AuthorSchema(halogen.Schema):\n name = halogen.Attr(required=True)\n\n\n class PublisherSchema(halogen.Schema):\n name = halogen.Attr(required=True)\n address = halogen.Attr()\n\n\n class BookSchema(halogen.Schema):\n title = halogen.Attr(required=True)\n year = halogen.Attr(halogen.types.Int(), required=True)\n authors = halogen.Attr(halogen.types.List(AuthorSchema), required=True)\n publisher = halogen.Attr(PublisherSchema)\n\n try:\n BookSchema.deserialize(\n dict(\n # title is skipped\n year=\"abc\", # Not an integer\n authors=[dict(name=\"John Smith\"), dict()], # Second author has no name\n publisher=dict(address=\"Chasey Lane 42, Los Angeles, US\"), # No name\n ),\n )\n except halogen.exceptions.ValidationError as e:\n error = Error.from_validation_exception(e)\n\n >>> error.errors\n >>>\n [\n {\"path\": \"/authors/1/name\", \"message\": \"Missing attribute.\"),\n {\"path\": \"/title\", \"message\": \"Missing attribute.\"),\n {\"path\": \"/year\", \"message\": \"'abc' is not an integer\"),\n {\"path\": \"/publisher/name\", \"message\": \"Missing attribute.\"),\n }\n\n\nThe errors may or may not be related to the payload, but sometimes to another resources.\nIn this case the about link should be returned within the error.\n\n.. code-block:: python\n\n {\n \"_links\": {\n \"about\": {\"href\": \"/products/1\"}\n },\n \"message\": \"The product is sold out.\"\n }\n\ni18n\n----\n\nThe error messages should be internationalized and respect Accept-Language and Content-Language HTTP headers.\n\n\nContact\n=======\n\nIf you have questions, bug reports, suggestions, etc. please create an issue on\nthe `GitHub project page `_.\n\n\nLicense\n=======\n\nThis software is licensed under the `MIT license `_\n\nSee `License file `_\n\n\n\u00a9 2013 Oleg Pidsadnyi, Paylogic International and others.\n\nChangelog\n=========\n\n1.5.0\n-----\n\n* Allow passing context keyword arguments to deserialize methods (blaise-io)\n\n\n1.4.1\n-----\n\n* Fix the package setup (olegpidsadnyi)\n\n\n1.4.0\n-----\n\n* Support for vnd.error responses (olegpidsadnyi)\n\n\n1.3.5\n-----\n\n* Add ISO Date Time type (moisesribeiro)\n\n\n1.3.4\n-----\n\n* Nullable type (olegpidsadnyi)\n\n\n1.3.3\n-----\n\n* Strict validation of the ISO8601 (olegpidsadnyi)\n\n1.3.2\n-----\n\n* Improve serialization performance (youtux)\n\n\n1.3.1\n-----\n\n* Fix for the String.deserialize to force the text type (olegpidsadnyi)\n\n\n1.3.0\n-----\n\n* Attribute as a decorator (olegpidsadnyi)\n\n\n1.2.1\n-----\n\n* Use native datetime.isoformat for datetime serialization (bubenkoff)\n\n1.1.3\n-----\n\n* Correctly handle schema class derivation (bubenkoff)\n\n1.1.2\n-----\n\n* Correct deserialization for String and Int types (bubenkoff)\n\n1.1.1\n-----\n\n* Deprecation attribute is added to Link (olegpidsadnyi)\n\n1.1.0\n-----\n\n* Add common-use types (bubenkoff)\n\n1.0.8\n-----\n\n* Correctly handle and document ``required`` and ``default`` (bubenkoff)\n* Properly get validator's comparison values (lazy and constant) (bubenkoff)\n* Increase test coverage (bubenkoff)\n\n1.0.6\n-----\n\n* Respect ValueError in deserialization of the attributes (bubenkoff)\n\n1.0.4\n-----\n\n* Correctly render and document deserialization errors (bubenkoff)\n\n1.0.3\n-----\n\n* Allow Embedded fields to be marked as not required (mattupstate)\n* Field order is preserved in serialized documents (mattupstate)\n\n1.0.0\n-----\n\n* Initial public release\n\n\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/paylogic/halogen", "keywords": "", "license": "MIT license", "maintainer": "", "maintainer_email": "", "name": "halogen", "package_url": "https://pypi.org/project/halogen/", "platform": "", "project_url": "https://pypi.org/project/halogen/", "project_urls": { "Homepage": "https://github.com/paylogic/halogen" }, "release_url": "https://pypi.org/project/halogen/1.5.0/", "requires_dist": [ "cached-property", "isodate", "python-dateutil", "pytz", "six" ], "requires_python": "", "summary": "Python HAL generation/parsing library", "version": "1.5.0" }, "last_serial": 5090248, "releases": { "0.0.1": [], "0.0.2": [ { "comment_text": "", "digests": { "md5": "2788f7e458aa7a14962d1562645babcf", "sha256": "f10d4209f5cdbdf1b2e225519d2fb64aa425285b5de181015f4ed25d8208097e" }, "downloads": -1, "filename": "halogen-0.0.2.tar.gz", "has_sig": false, "md5_digest": "2788f7e458aa7a14962d1562645babcf", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 5464, "upload_time": "2014-05-13T14:33:56", "url": "https://files.pythonhosted.org/packages/6c/fb/e2bebd56ba4a228acca9e987cf1dfa939bd1ae957fe2e847fa27f69883bf/halogen-0.0.2.tar.gz" } ], "0.0.3": [ { "comment_text": "", "digests": { "md5": "5d539d13f7bcba15f9c52bfede772968", "sha256": "fcdb5480e2cc1c39bc39a937337f5815087b767c35e05b60a28776eb801022f6" }, "downloads": -1, "filename": "halogen-0.0.3.tar.gz", "has_sig": false, "md5_digest": "5d539d13f7bcba15f9c52bfede772968", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6851, "upload_time": "2014-06-02T11:33:09", "url": "https://files.pythonhosted.org/packages/dc/bc/4b70398c3d035642a041bccbfd74da42ae59a8257141d028ed7607a3dee6/halogen-0.0.3.tar.gz" } ], "0.1.2": [ { "comment_text": "", "digests": { "md5": "c217a94008e3137ad83e8f6df3ad9dbf", "sha256": "a47084329659b1483593d67820e594d227e50fab72f962bf61da5dfbdaee1d8b" }, "downloads": -1, "filename": "halogen-0.1.2.tar.gz", "has_sig": false, "md5_digest": "c217a94008e3137ad83e8f6df3ad9dbf", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 17364, "upload_time": "2014-10-30T08:37:49", "url": "https://files.pythonhosted.org/packages/88/b9/89c2c8bb6c084b540461c6224fb2cefd99e403caa76cee936cef2787dfea/halogen-0.1.2.tar.gz" } ], "1.0.3": [ { "comment_text": "", "digests": { "md5": "a79818a874a95834e2c4ace8fba3e0f0", "sha256": "85c368cd1df827df9ad73d5950a728546c89cb6d58bb5266f1ec43193e744558" }, "downloads": -1, "filename": "halogen-1.0.3.tar.gz", "has_sig": false, "md5_digest": "a79818a874a95834e2c4ace8fba3e0f0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13530, "upload_time": "2014-12-30T20:48:38", "url": "https://files.pythonhosted.org/packages/e1/55/cd553fa49ca844efb7bdcf0622746a93ec4b3eb247fa3659511e1e347c80/halogen-1.0.3.tar.gz" } ], "1.0.4": [ { "comment_text": "", "digests": { "md5": "4b9b7b391966d9c1658539b22094051a", "sha256": "0a52efd1b72f311f260c350f82b190aac72286636694e0521884b3bd2cced060" }, "downloads": -1, "filename": "halogen-1.0.4.tar.gz", "has_sig": false, "md5_digest": "4b9b7b391966d9c1658539b22094051a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20754, "upload_time": "2015-03-27T11:58:08", "url": "https://files.pythonhosted.org/packages/44/e5/5f967d94ea943827c1eff066510b632729e466f1ccda94222d5ba09bd046/halogen-1.0.4.tar.gz" } ], "1.0.5": [ { "comment_text": "", "digests": { "md5": "ca49dfc64e5e3677bc3125d842906644", "sha256": "38633b4280fa1fa45b1ff08e0658491bb18eb20ca29965518ff03ac8b6de4afe" }, "downloads": -1, "filename": "halogen-1.0.5.tar.gz", "has_sig": false, "md5_digest": "ca49dfc64e5e3677bc3125d842906644", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21166, "upload_time": "2015-03-27T13:48:56", "url": "https://files.pythonhosted.org/packages/8c/e7/68a16b38b4a5508b6a0bbd9299ad8d94fc79bd58dfc7f67a6c119672a1f9/halogen-1.0.5.tar.gz" } ], "1.0.6": [ { "comment_text": "", "digests": { "md5": "9c28ff9ddc714a6401545d4c370d138f", "sha256": "f7f86d05ec1fa5ebb5c5ea8c60cc4610f6022e42e228e741018961ca99a58e6a" }, "downloads": -1, "filename": "halogen-1.0.6.tar.gz", "has_sig": false, "md5_digest": "9c28ff9ddc714a6401545d4c370d138f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21172, "upload_time": "2015-03-27T15:05:39", "url": "https://files.pythonhosted.org/packages/ea/21/2afabafdfbe6c6ea1abaed13b13a7274c8079dea58e88356a71eb3ee14a7/halogen-1.0.6.tar.gz" } ], "1.0.7": [ { "comment_text": "", "digests": { "md5": "611477e4085246aa838a564df26f4863", "sha256": "3c5e6cb206158ee55daaaf9e8983e22f089e823c8421bfa450c911aa8dad88a7" }, "downloads": -1, "filename": "halogen-1.0.7.tar.gz", "has_sig": false, "md5_digest": "611477e4085246aa838a564df26f4863", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 22111, "upload_time": "2015-03-28T03:44:44", "url": "https://files.pythonhosted.org/packages/fc/f8/b0c6428776ac2e0af0d86d0e775544be83c3b6f4fdd9b295ebfa8a6259e4/halogen-1.0.7.tar.gz" } ], "1.0.8": [ { "comment_text": "", "digests": { "md5": "4ece0c6703b90cc7cd4a7ad59e584baa", "sha256": "716f99de86c40244944a0bde559dab6d92c5aea25375a672c0534ffdc9bce455" }, "downloads": -1, "filename": "halogen-1.0.8.tar.gz", "has_sig": false, "md5_digest": "4ece0c6703b90cc7cd4a7ad59e584baa", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 22347, "upload_time": "2015-03-28T04:20:20", "url": "https://files.pythonhosted.org/packages/9c/fb/4f694d8cbaea3c4557d24f77b552ce588674a4d75e2ccdbc0528355bf1dc/halogen-1.0.8.tar.gz" } ], "1.1.0": [ { "comment_text": "", "digests": { "md5": "c8bfcdef450d4bb77995b4c9d580ddef", "sha256": "dc8306d4b580ffb4788187be4df4facf532307a08aa2af8e7d786943ef3166bb" }, "downloads": -1, "filename": "halogen-1.1.0.tar.gz", "has_sig": false, "md5_digest": "c8bfcdef450d4bb77995b4c9d580ddef", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 24026, "upload_time": "2015-03-31T09:49:22", "url": "https://files.pythonhosted.org/packages/0f/05/f9f9c376aa9fec5202359542d471a572935689851c2a90652c7aa76b03de/halogen-1.1.0.tar.gz" } ], "1.1.1": [ { "comment_text": "", "digests": { "md5": "59f81bbc9a696f7df196377c1fe200c8", "sha256": "4ec623529e6b8e49cb14631ec32dc3b8ec95e15cbf77ddb97e5b5bd286ec30cc" }, "downloads": -1, "filename": "halogen-1.1.1.tar.gz", "has_sig": false, "md5_digest": "59f81bbc9a696f7df196377c1fe200c8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 24649, "upload_time": "2015-06-29T12:28:41", "url": "https://files.pythonhosted.org/packages/08/df/89e79c456ee1a2052a0e6367e7f093522c88743628344779dbc4aa2eb9a4/halogen-1.1.1.tar.gz" } ], "1.1.2": [ { "comment_text": "", "digests": { "md5": "82a50d91ebab5de7c140969a0ba01b69", "sha256": "b564a6332585baa4b4c6cfa95cdb2ad68f98e0b341c3f48fc0c170c416a2d4ff" }, "downloads": -1, "filename": "halogen-1.1.2.tar.gz", "has_sig": false, "md5_digest": "82a50d91ebab5de7c140969a0ba01b69", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 24564, "upload_time": "2015-09-14T19:31:26", "url": "https://files.pythonhosted.org/packages/1a/4b/c6295f9abb5fafc7679c59dfbc8077c68f8c899d27410c68df9a17aaef06/halogen-1.1.2.tar.gz" } ], "1.1.3": [ { "comment_text": "", "digests": { "md5": "5801bb29769c6cc16314b1b7cac23143", "sha256": "58704ee2389898cf7462635ee83173220f3b663961a9657edaddabc34c854196" }, "downloads": -1, "filename": "halogen-1.1.3.tar.gz", "has_sig": false, "md5_digest": "5801bb29769c6cc16314b1b7cac23143", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 24648, "upload_time": "2015-10-02T14:35:57", "url": "https://files.pythonhosted.org/packages/2a/76/48ce494e7fc17116e0ce7ed209bc8d26559556f34da6293a4c350ad742a7/halogen-1.1.3.tar.gz" } ], "1.2.0": [ { "comment_text": "", "digests": { "md5": "7e1f46643c54cda4ad79ee67b8bc365d", "sha256": "ce03377951b4dc32c79511e9744c72b1202b86630bd8ab1d8ba2b53bd6f39a0c" }, "downloads": -1, "filename": "halogen-1.2.0.tar.gz", "has_sig": false, "md5_digest": "7e1f46643c54cda4ad79ee67b8bc365d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 24729, "upload_time": "2015-10-30T14:52:01", "url": "https://files.pythonhosted.org/packages/64/eb/f400d15028ccdaa45088216a3310a1aa26cb285db35d042e451872816ce8/halogen-1.2.0.tar.gz" } ], "1.3.0": [ { "comment_text": "", "digests": { "md5": "31aadc131828678134744c656b2d9133", "sha256": "45136e6406a3336740dd252d8ec6800e1df3d33095dd896506cdeaa011422ad7" }, "downloads": -1, "filename": "halogen-1.3.0.tar.gz", "has_sig": false, "md5_digest": "31aadc131828678134744c656b2d9133", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 26088, "upload_time": "2016-06-10T13:32:32", "url": "https://files.pythonhosted.org/packages/19/8b/9b31078a4ef5687c2c5641ff181798a40d27cb2c29f4037b5a2e5270372c/halogen-1.3.0.tar.gz" } ], "1.3.1": [ { "comment_text": "", "digests": { "md5": "ba00081c6af2d45371326f1dfaee13d1", "sha256": "4834344cd832f0981c672bb84c96b02481def4546952c6e628f8a661e853d01c" }, "downloads": -1, "filename": "halogen-1.3.1.tar.gz", "has_sig": false, "md5_digest": "ba00081c6af2d45371326f1dfaee13d1", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 26367, "upload_time": "2017-01-11T12:10:55", "url": "https://files.pythonhosted.org/packages/6c/21/03ac268d4b0100e4e9f976dc4473de6ddaf17dd068b0a201740884cf8357/halogen-1.3.1.tar.gz" } ], "1.3.2": [ { "comment_text": "", "digests": { "md5": "e301f603105136a3bba4ab5b851bb517", "sha256": "ae5dda120d0c978d537bca837ee46bf48a62ad730db1c7de64929310ce4e9cf1" }, "downloads": -1, "filename": "halogen-1.3.2.tar.gz", "has_sig": false, "md5_digest": "e301f603105136a3bba4ab5b851bb517", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 26733, "upload_time": "2017-03-06T16:08:49", "url": "https://files.pythonhosted.org/packages/a7/10/0edc5251624170ee4e15ddd2e8b3595180ea4a41a2aa4e9e1ae3b00a4bd9/halogen-1.3.2.tar.gz" } ], "1.3.3": [ { "comment_text": "", "digests": { "md5": "63b0da2f6febbb1785b0fbdff17189bf", "sha256": "7466a6c8cc412f8da5f64d14483c927c14227337eeac61a1850454bed07c41cf" }, "downloads": -1, "filename": "halogen-1.3.3.tar.gz", "has_sig": false, "md5_digest": "63b0da2f6febbb1785b0fbdff17189bf", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 26833, "upload_time": "2017-05-29T12:46:41", "url": "https://files.pythonhosted.org/packages/da/59/f76cdb199d1ca7366b1007cff0ab99b24f77d1130865a3f17c0a96ae3cf3/halogen-1.3.3.tar.gz" } ], "1.3.4": [ { "comment_text": "", "digests": { "md5": "6cc1e670c1fbceae6230e5ecda8f1140", "sha256": "97630af2d2a26eda7b694e70fe54bbb4bb616b138eec625abc4c0e6264746e78" }, "downloads": -1, "filename": "halogen-1.3.4.tar.gz", "has_sig": false, "md5_digest": "6cc1e670c1fbceae6230e5ecda8f1140", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 29079, "upload_time": "2018-04-09T10:57:02", "url": "https://files.pythonhosted.org/packages/f7/23/e0fc4d7e8d3473b4b3ccc99f9f33a3012a0717ba920d1723bbfb6c7a1507/halogen-1.3.4.tar.gz" } ], "1.3.5": [ { "comment_text": "", "digests": { "md5": "287afe6561dd43b993cf838b66f62db0", "sha256": "70be735c4a19cfeef6b3296dc1c29221ad06d92eaf371913e1a17bdee27ea70b" }, "downloads": -1, "filename": "halogen-1.3.5.tar.gz", "has_sig": false, "md5_digest": "287afe6561dd43b993cf838b66f62db0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 29213, "upload_time": "2018-04-13T07:33:58", "url": "https://files.pythonhosted.org/packages/09/c9/45c7b5431a77589129593be75626ec769feb7c905a94914046fd38abe9cb/halogen-1.3.5.tar.gz" } ], "1.4.0": [ { "comment_text": "", "digests": { "md5": "0d87f979b4c25a86751346a6b4e37d6b", "sha256": "8a90300334f7821f32dddacfba57f1a5885908f4f26a36995f572fac0a862581" }, "downloads": -1, "filename": "halogen-1.4.0-py2-none-any.whl", "has_sig": false, "md5_digest": "0d87f979b4c25a86751346a6b4e37d6b", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 18398, "upload_time": "2019-02-19T08:24:10", "url": "https://files.pythonhosted.org/packages/81/cd/b5698a4f5383a07e8dbe63bcbe8bfde7fdee18ebe2d5b048003bc0ca5bfd/halogen-1.4.0-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "c6f74580a8644daecb0fd1964757b430", "sha256": "5a8b6443129191ee642eb3b323a1d73d8c37a4263f6fceaa3528b47278d10af8" }, "downloads": -1, "filename": "halogen-1.4.0.tar.gz", "has_sig": false, "md5_digest": "c6f74580a8644daecb0fd1964757b430", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 31474, "upload_time": "2019-02-19T08:22:47", "url": "https://files.pythonhosted.org/packages/54/08/c699a70e9233153724a456e425d7c875816dd53881958e2b70a456d041f7/halogen-1.4.0.tar.gz" } ], "1.4.1": [ { "comment_text": "", "digests": { "md5": "04cbdb7fc398d7e6c40ecfad77ccacf2", "sha256": "2c299c549c177b57f5625cdeb41db9c247062f60177b4977c0e0de7e9a48e870" }, "downloads": -1, "filename": "halogen-1.4.1-py2-none-any.whl", "has_sig": false, "md5_digest": "04cbdb7fc398d7e6c40ecfad77ccacf2", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 19487, "upload_time": "2019-02-19T11:55:28", "url": "https://files.pythonhosted.org/packages/18/af/fcf98dde2c4a2f48ce47178acde4ccd263ad7f01b5e51cd7521c3302225e/halogen-1.4.1-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "190e99a744c95d9f18c6f55255830ac1", "sha256": "04c51c6c80479e1b709da1e6c3248a5e332dadba049fcb9d58658700051d2e46" }, "downloads": -1, "filename": "halogen-1.4.1.tar.gz", "has_sig": false, "md5_digest": "190e99a744c95d9f18c6f55255830ac1", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 32070, "upload_time": "2019-02-19T11:55:30", "url": "https://files.pythonhosted.org/packages/bb/10/95629d1a2980e70be9022d9f3629235db4415de22b86a46b83925ddc1984/halogen-1.4.1.tar.gz" } ], "1.5.0": [ { "comment_text": "", "digests": { "md5": "29d9e5268d453d6236d7eea3cb31f120", "sha256": "e4463dec48a5bb7d2d7fe96ca17a541b556091f26d08b2cdd09b2732487a2bc5" }, "downloads": -1, "filename": "halogen-1.5.0-py2-none-any.whl", "has_sig": false, "md5_digest": "29d9e5268d453d6236d7eea3cb31f120", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 19977, "upload_time": "2019-04-03T10:59:33", "url": "https://files.pythonhosted.org/packages/67/32/e6a2bc6ce06463d6c6799ec2fb52382dae746f171b02dd97f1f8cc496377/halogen-1.5.0-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "c16cbdca2cf7f0dd6059a081263e71df", "sha256": "7240e4987b8ee3ac9a86f03f68cb130a17fa34ddc39a68238bd0902cef135454" }, "downloads": -1, "filename": "halogen-1.5.0.tar.gz", "has_sig": false, "md5_digest": "c16cbdca2cf7f0dd6059a081263e71df", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 33695, "upload_time": "2019-04-03T10:59:35", "url": "https://files.pythonhosted.org/packages/0e/24/9ff8235d658a8e7f8a93a8b1e646194a424a687e682e72fb0c193a869101/halogen-1.5.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "29d9e5268d453d6236d7eea3cb31f120", "sha256": "e4463dec48a5bb7d2d7fe96ca17a541b556091f26d08b2cdd09b2732487a2bc5" }, "downloads": -1, "filename": "halogen-1.5.0-py2-none-any.whl", "has_sig": false, "md5_digest": "29d9e5268d453d6236d7eea3cb31f120", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 19977, "upload_time": "2019-04-03T10:59:33", "url": "https://files.pythonhosted.org/packages/67/32/e6a2bc6ce06463d6c6799ec2fb52382dae746f171b02dd97f1f8cc496377/halogen-1.5.0-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "c16cbdca2cf7f0dd6059a081263e71df", "sha256": "7240e4987b8ee3ac9a86f03f68cb130a17fa34ddc39a68238bd0902cef135454" }, "downloads": -1, "filename": "halogen-1.5.0.tar.gz", "has_sig": false, "md5_digest": "c16cbdca2cf7f0dd6059a081263e71df", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 33695, "upload_time": "2019-04-03T10:59:35", "url": "https://files.pythonhosted.org/packages/0e/24/9ff8235d658a8e7f8a93a8b1e646194a424a687e682e72fb0c193a869101/halogen-1.5.0.tar.gz" } ] }