{ "info": { "author": "Phillip J. Eby", "author_email": "peak@eby-sarna.com", "bugtrack_url": null, "classifiers": [], "description": "==========================================================\nPackage Shipment and Address Verification with ``PyDicia``\n==========================================================\n\nNew in 0.1a3\n * Added ``FlatRateLargeBox`` and ``ExpressMailPremiumService`` tags, for use\n with DAZzle 8.0 and up.\n\nNew in 0.1a2\n * Dropped MS Windows requirement; you can now use PyDicia to generate XML for\n the Endicia Mac client, or to be sent via download or networked directory\n to a Windows client. \n\n * Added a ``Shipment.run()`` method to allow running multiple batches in\n sequence.\n\n * Fixed the ``Status.ToZip4`` attribute (it was incorrectly spelled\n ``ToZIP4``, and thus didn't work).\n \nPyDicia is a Python interface to endicia.com's postal services client, DAZzle.\nUsing DAZzle's XML interface, PyDicia can be used to print shipping labels,\nenvelopes, postcards and more, with or without prepaid US postage indicia\n(electronic stamps), as well as doing address verification.\n\nIn addition to providing a layer of syntax sugar for the DAZzle XML interface,\nPyDicia provides a novel adaptive interface that lets you smoothly integrate\nits functions with your application's core types (like invoice, customer, or\n\"packing slip\" objects) without subclassing. (This is particularly useful if\nyou are extending a CRM or other database that was written by somebody else.)\n\nThis version of PyDicia is an alpha proof-of-concept release. It is actually\nusable -- I've in fact been using it in production for over a year, shipping\ndozens of envelopes and packages a month all over the US, Canada, and Europe.\nHowever, the API is still potentially subject to change, the reference\ndocumentation is sketchy, and the developer's guide lacks detail about some of\nthe more advanced features. This should improve in future releases, but at\nleast this way, you can use it now, if you need something like it. Reading the\n`DAZzle XML API specification`_ is a good idea if you want to use this, though;\nthe 8.0 version is the latest, although PyDicia also supports 7.0.x versions.\n\n.. _DAZzle XML API specification: http://fileserver.surveygizmo.com/users/4508/dazzleXMLInterfaceV8.pdf\n\nPyDicia uses the ElementTree, simplegeneric, and DecoratorTools packages, and\nrequires Python 2.4 or higher (due to use of decorators and the ``Decimal``\ntype). Actually printing any labels requires that you have an Endicia\n\"Premium\" or \"Mac\" account. (Note: I have not used the Mac client, so I don't\nknow how well it works there. See the section below on `Using PyDicia on\nNon-Windows Platforms`_ for more info.)\n\nIMPORTANT\n Please note that PyDicia does not attempt to implement all of the US Postal\n Service's business rules for what options may be used in what combinations.\n It doesn't even validate most of the DAZzle client's documented\n restrictions! So it's strictly a \"Garbage In, Garbage Out\" kind of deal.\n If you put garbage in, who knows what the heck will happen. You might end\n up spending lots of money *and* getting your packages returned to you --\n and **I AM NOT RESPONSIBLE**, even if your problem is due to an error in\n PyDicia or its documentation!\n\n So, make sure you understand the shipping options you wish to use, and test\n your application thoroughly before using this code in production. You have\n been warned!\n\nQuestions, discussion, and bug reports for this software should be directed to\nthe PEAK mailing list; see http://www.eby-sarna.com/mailman/listinfo/PEAK/\nfor details.\n\n.. contents:: **Table of Contents**\n\n\n-----------------\nDeveloper's Guide\n-----------------\n\n\nBasic XML Generation\n====================\n\nPyDicia simplifies the creation of XML for DAZzle by using objects to specify\nwhat data needs to go in the XML. These objects are mostly ``Option``\ninstances, or callables that create ``Option`` instances. However, the\nframework is extensible, so that you can use your own object types with the\nsame API. Your object types can either generate ``Option`` instances, or\ndirectly manipulate the XML using ElementTree APIs for maximum control.\n\nIn the simpler cases, however, you will just use lists or tuples of objects\nprovided by (or created with) the PyDicia API to represent packages or labels.\n\n\nBatch Objects\n-------------\n\nXML documents are represented using ``Batch`` objects::\n\n >>> from pydicia import *\n >>> b = Batch()\n\nThe ``tostring()`` method of a batch returns its XML in string form, optionally\nin a given encoding (defaulting to ASCII if not specified)::\n\n >>> print b.tostring('latin1')\n \n \n\nTo add a package to a batch, you use the ``add_package()`` method::\n\n >>> b.add_package(ToName('Phillip Eby'))\n >>> print b.tostring()\n \n \n Phillip Eby\n \n \n\nThe ``add_package()`` method accepts zero or more objects that can manipulate\nPyDicia package objects. It also accepts tuples or lists of such objects,\nnested to arbitrary depth::\n\n >>> b.add_package([Services.COD, (Stealth, ToName('Ty Sarna'))], FlatRateBox)\n\n >>> print b.tostring()\n \n \n Phillip Eby\n \n \n \n TRUE\n Ty Sarna\n FLATRATEBOX\n \n \n\nAnd the ``packages`` attribute of a batch keeps track of the arguments that\nhave been passed to ``add_package()``::\n\n >>> b.packages\n [Package(ToName('Phillip Eby'),),\n Package([Services.COD('ON'), (Stealth('TRUE'), ToName('Ty Sarna'))],\n PackageType('FLATRATEBOX'))]\n\nEach package object in the list wraps a tuple of the arguments that were\nsupplied for each invocation of ``add_package()``. This allows the system\nto send status updates (including delivery confirmation numbers, customs IDs,\netc.) back to the application.\n\nBut before we can process status updates, we need to have some application\nobjects, as described in the next section.\n\n\nUsing Your Application Objects as Package Sources\n-------------------------------------------------\n\nIn addition to PyDicia-defined objects and sequences thereof, the\n``add_package()`` method also accepts any custom objects of your own design\nthat have been registered with the ``pydicia.add_to_package()`` or\n``pydicia.iter_options()`` generic functions::\n\n >>> class Customer:\n ... def __init__(self, **kw):\n ... self.__dict__ = kw\n\n >>> @iter_options.when_type(Customer)\n ... def cust_options(ob):\n ... yield ToName(ob.name)\n ... yield ToAddress(ob.address)\n ... yield ToCity(ob.city)\n ... yield ToState(ob.state)\n ... yield ToPostalCode(ob.zip)\n\n >>> b = Batch()\n >>> c = Customer(\n ... name='PJE', address='123 Nowhere Dr', state='FL', city='Nowhere',\n ... zip='12345-6789'\n ... )\n >>> b.add_package(c)\n >>> print b.tostring()\n \n \n PJE\n 123 Nowhere Dr\n Nowhere\n FL\n 12345-6789\n \n \n\nThis allows you to pass customer, package, product, invoice, or other\napplication-specific objects into ``add_package()``. And the objects yielded\nby your ``iter_options`` implementation can also be application objects, e.g.::\n\n >>> class Invoice:\n ... def __init__(self, **kw):\n ... self.__dict__ = kw\n\n >>> @iter_options.when_type(Invoice)\n ... def invoice_options(ob):\n ... yield ob.shippingtype\n ... yield ob.products\n ... yield ob.customer\n\n >>> b = Batch()\n >>> i = Invoice(\n ... shippingtype=(Tomorrow, MailClass('MEDIAMAIL')),\n ... products=[WeightOz(27),], customer=c\n ... )\n >>> b.add_package(i)\n >>> print b.tostring()\n \n \n 1\n MEDIAMAIL\n 27\n PJE\n 123 Nowhere Dr\n Nowhere\n FL\n 12345-6789\n \n \n\nNote that there is no particular significance to my choice of lists vs. tuples\nin these examples; they're more to demonstrate that you can use arbitrary\nstructures, as long as they contain objects that are supported by either\n``iter_options()`` or ``add_to_package()``. Normally, you will simply use\ncollections of either PyDicia-provided symbols, or application objects for\nwhich you've defined an ``iter_options()`` method.\n\nYou will also usually want to implement your PyDicia support in a module by\nitself, so you can use ``from pydicia import *`` without worrying about symbol\ncollisions.\n\n\nBatch-wide Options\n------------------\n\nWhen you create a batch, you can pass in any number of objects, to specify\noptions that will be applied to every package. For example, this batch will\nhave every package set to be mailed tomorrow as media mail::\n\n >>> b = Batch( Tomorrow, MailClass('MEDIAMAIL') )\n >>> b.add_package(ToName('PJE'))\n >>> print b.tostring()\n \n \n PJE\n 1\n MEDIAMAIL\n \n \n\n\nMulti-Batch Shipments\n=====================\n\nCertain DAZzle options can only be set once per XML file, such as the choice of\nlayout file. If you are shipping multiple packages with different label\nlayouts (such as domestic vs. international mail), you need to separate these\npackages into different batches, each of which will be in a separate XML file.\nThe ``Shipment`` class handles this separation for you automatically.\n\nWhen you create a shipment, it initially has no batches::\n\n >>> s = Shipment()\n >>> s.batches\n []\n\n\nBut as you add packages to it, it will create batches as needed::\n\n >>> s.add_package(ToName('Phillip Eby'), DAZzle.Test)\n >>> len(s.batches)\n 1\n\n >>> print s.batches[0].tostring()\n \n \n Phillip Eby\n \n \n\nAs long as you're adding packages with the same or compatible options, the\nsame batch will be reused::\n\n >>> s.add_package(ToName('Ty Sarna'), DAZzle.Test)\n >>> len(s.batches)\n 1\n >>> print s.batches[0].tostring()\n \n \n Phillip Eby\n \n \n Ty Sarna\n \n \n\nBut as soon as you add a package with any incompatible options, a new batch\nwill be created and used::\n\n >>> s.add_package(ToName('PJE'), ~DAZzle.Test)\n >>> len(s.batches)\n 2\n\n >>> print s.batches[1].tostring()\n \n \n PJE\n \n \n\nAnd each time you add a package, it's added to the first compatible batch::\n\n >>> s.add_package(ToName('Some Body'), ~DAZzle.Test)\n >>> len(s.batches)\n 2\n\n >>> print s.batches[1].tostring()\n \n \n PJE\n \n \n Some Body\n \n \n\n >>> s.add_package(ToName('No Body'), DAZzle.Test)\n >>> len(s.batches)\n 2\n\n >>> print s.batches[0].tostring()\n \n \n Phillip Eby\n \n \n Ty Sarna\n \n \n No Body\n \n \n\nBy the way, as with batches, you can create a shipment with options that will\nbe applied to all packages::\n\n >>> s = Shipment(Tomorrow, Services.COD)\n >>> s.add_package(ToName('Some Body'), DAZzle.Test)\n >>> s.add_package(ToName('No Body'), ~DAZzle.Test)\n >>> len(s.batches)\n 2\n >>> print s.batches[0].tostring()\n \n \n Some Body\n 1\n \n \n \n\n >>> print s.batches[1].tostring()\n \n \n No Body\n 1\n \n \n \n\n\n\nReceiving Status Updates\n========================\n\nWhen DAZzle completes a batch, it creates an output file containing status\ninformation for each package in the batch. If you'd like to process this\nstatus information for the corresponding application objects you passed in\nto ``add_package()``, you can extend the ``report_status()`` generic function\nto do this::\n\n >>> @report_status.when_type(Customer)\n ... def customer_status(ob, status):\n ... print ob\n ... print status\n\n >>> b = Batch()\n >>> b.add_package(c)\n\nWhen the batch receives status information, it will invoke ``report_status()``\non each package's application items, with a status object for the corresponding\npackage::\n\n >>> b.report_statuses()\n <...Customer instance...>\n ToAddress : [u'123 Nowhere Dr']\n ToCity : u'Nowhere'\n ToState : u'FL'\n ToPostalCode : u'12345-6789'\n ToAddress1 : u'123 Nowhere Dr'\n\nNote that you don't normally need to call ``report_statuses()`` directly; it's\nusually done for you as part of the process of running a batch or shipment.\n(See the section below on `Invoking DAZzle`_.)\n\nThe `status` object passed to your method will be a ``Status`` instance with\nattributes similar to those above, containing USPS-normalized address data.\nIn addition, several other fields are possible::\n\n >>> from pydicia import ET\n >>> b.etree = ET.fromstring('''\n ... \n ... 1234\n ... Rejected (-3)\n ... 123465874359\n ... 4.60\n ... 20070704173221\n ... 20070705\n ... ''')\n\n >>> b.report_statuses()\n <...Customer instance...>\n Status : 'Rejected (-3)'\n ErrorCode : -3\n ToAddress : []\n ToZip4 : '1234'\n PIC : '123465874359'\n FinalPostage : Decimal(\"4.60\")\n TransactionDateTime : datetime.datetime(2007, 7, 4, 17, 32, 21)\n PostmarkDate : datetime.date(2007, 7, 5)\n\nThe ``Status`` object should support all output fields supported by DAZzle; see\nthe DAZzle documentation for details. The non-string fields shown above are\nthe only ones which are postprocessed to specialized Python objects; the rest\nare kept as strings or Unicode values. The ``ErrorCode`` field is computed by\nextracting the integer portion of any rejection code. It is ``None`` in the\ncase of a successful live print, and ``0`` in the case of a successful test\nprint. See the DAZzle XML interface documentation for a description of other\nerror codes.\n\nNote that for a more compact presentation, attributes with ``None`` values are\nnot included in the ``str()`` of a ``Status`` object, which is why the statuses\ndisplayed above show different sets of fields. The attributes, however, always\nexist; they simply have ``None`` as their value.\n\n\nInvoking DAZzle\n===============\n\nIn the simplest case, invoking a batch or shipment object's ``.run()`` method\nwill launch a local copy of DAZzle on a temporary file containing the batch's\nXML, wait for DAZzle to exit, then process status updates from the output file\nand return DAZzle's return code. (Or a list of return codes, in the case of a\n``Shipment``.)\n\nIf you are using this approach, you may wish to include ``~DAZzle.Prompt``\n(which keeps end-user prompts to a minimum) and ``DAZzle.AutoClose`` (so that\nDAZzle exits upon completion of the batch) in your batch options.\n\nIf you do not have a local copy of DAZzle, but instead are using a network\nqueue directory to send jobs remotely, you can instead use the batch object's\n``.write(queuedir)`` method to send the batch to the queue. You can also\nuse this approach to send jobs to a local copy of DAZzle running in the\nbackground.\n\nIf a copy of DAZzle is installed locally, you can get its XML queue directory\nfrom ``DAZzle.XMLDirectory``, and check whether it is monitoring for files\nusing ``DAZzle.get_preference(\"MonitorXML\")``. (These values will be ``None``\nif DAZzle is not installed.)\n\nIf DAZzle is installed locally, you can launch it with the\n``DAZzle.run(args=(), sync=True)`` function. The `args` are a list of command\nline arguments to pass, and `sync` is a flag indicating whether to wait for\nDAZzle to exit. If `sync` is a false value, ``run()`` returns a\n``subprocess.Popen`` instance. Otherwise, it returns the process's exit code\n(ala ``subprocess.call``).\n\nXXX async batch status retrieval\n\nXXX DAZzle.exe_path, DAZzle.get_preference(), DAZzle.LayoutDirectory\n\nXXX Launching for multi-batch, remote, queued, and other async processing\n\n\nUsing PyDicia on Non-Windows Platforms\n======================================\n\nWhen used on a non-Windows platform, PyDicia cannot detect any DAZzle\nconfiguration information, so you must manually set ``DAZzle.exe_path`` to\nthe client program, if you wish to use any of the ``run()`` methods.\n(Likewise, you must manually set ``DAZzle.LayoutDirectory`` if you want layout\npaths to be automatically adjusted.)\n\nOn the Mac, the ``exe_path`` should be set to a program that takes a single\nXML filename as an argument. The Mac ``endiciatool`` program probably will\nnot work on its own, without a wrapper shell script of some kind; I'm open to\nsuggestions as to how to improve this. (Note, by the way, that the Mac client\ndoesn't support all of the options that the Windows client does, so remember\nthat use of PyDicia is entirely at your own risk, whatever the platform!)\n\nOn other platforms, the main usefulness of PyDicia would be in generating XML\nfor users to download (e.g. from a web application) or submitting and\nprocessing jobs via a Samba-mounted queue directory. You don't need an\n``exe_path`` for this, but you will need to generate your own layout and output\nfile paths using ``Option`` objects, to avoid them being mangled by PyDicia's\nplatform-specific path munging.\n\nXXX explain how to do that, or make it work anyway\n\n\nAdvanced Customization\n======================\n\nXXX Using Option elements, add_to_package()\n\n\n-----------------\nOptions Reference\n-----------------\n\nBasic Package Options\n=====================\n\nXXX MailClass(text), NoPostage\nDateAdvance(), Today, Tomorrow\nValue()\nDescription()\nWeightOz()\n\n\nAddresses\n=========\n\n::\n >>> ToName(\"Phillip J. Eby\")\n ToName('Phillip J. Eby')\n\n >>> ToTitle(\"President\")\n ToTitle('President')\n\n >>> ToCompany(\"Dirt Simple, Inc.\")\n ToCompany('Dirt Simple, Inc.')\n\n\nXXX ToAddress(\\*lines)\nToCity(text), ToState(text), ToPostalCode(text), ToZIP4(text), ToCountry(text)\n\nXXX ReturnAddress(\\*lines)\nToDeliveryPoint(text)\nEndorsementLine(text)\nToCarrierRoute(text)\n\n\nPackage Details\n===============\n\nXXX PackageType()\nFlatRateEnvelope\nFlatRateBox\nRectangularParcel\nNonRectangularParcel\nPostcard\nFlat\nEnvelope\nWidth(), Length(), Depth()\nNonMachinable\nBalloonRate\n\n\nService Options\n===============\n\nXXX ReplyPostage\nStealth\nSignatureWaiver\nNoWeekendDelivery\nNoHolidayDelivery\nReturnToSender\nInsurance.USPS\nInsurance.Endicia\nInsurance.UPIC\nInsurance.NONE\nServices.RegisteredMail\nServices.CertifiedMail\nServices.RestrictedDelivery\nServices.CertificateOfMailing\nServices.ReturnReceipt\nServices.DeliveryConfirmation\nServices.SignatureConfirmation\nServices.COD\nServices.InsuredMail()\n\n\nCustoms Forms\n=============\n\nWhen processing international shipments, you will usually need to specify a\ncustoms form, contents type, and items. Additionally, if you want to print\nthe customs forms already \"signed\", you can specify a signer and the\ncertification option.\n\nContents Types\n--------------\n\nThe ``ContentsType`` constructor defines the type of contents declared on the\ncustoms form. There are six predefined constants for the standard contents\ntypes::\n\n >>> Customs.Sample\n ContentsType('SAMPLE')\n\n >>> Customs.Gift\n ContentsType('GIFT')\n\n >>> Customs.Documents\n ContentsType('DOCUMENTS')\n\n >>> Customs.Other\n ContentsType('OTHER')\n\n >>> Customs.Merchandise\n ContentsType('MERCHANDISE')\n\n >>> Customs.ReturnedGoods\n ContentsType('RETURNEDGOODS')\n\n\nCustoms Form Types\n------------------\n\nThe ``CustomsFormType`` constructor defines the type of customs form to be\nused. There are four predefined constants for the allowed form types::\n\n >>> Customs.GEM\n CustomsFormType('GEM')\n\n >>> Customs.CN22\n CustomsFormType('CN22')\n\n >>> Customs.CP72\n CustomsFormType('CP72')\n\n >>> Customs.NONE\n CustomsFormType('NONE')\n\n\nCustoms Items\n-------------\n\nItems to be declared on a customs form are created using ``Customs.Item``.\nThe minimum required arguments are a description, a unit weight in ounces\n(which must be an integer or decimal), and a value in US dollars (also an\ninteger or decimal)::\n\n >>> from decimal import Decimal\n >>> i = Customs.Item(\"Paperback book\", 12, Decimal('29.95'))\n\nYou may also optionally specify a quantity (which must be an integer) and a\ncountry of origin. The defaults for these are ``1`` and ``\"United States\"``,\nrespectively::\n\n >>> i\n Item('Paperback book', Decimal(\"12\"), Decimal(\"29.95\"), 1, 'United States')\n\nYou always specify a unit weight and value; these are automatically multiplied\nby the quantity on the customs form, and for purposes of calculating total\nweight/value.\n\nNote that a package's total weight must be greater than or equal to the sum of\nits items' weight, and its value must exactly equal the sum of its items'\nvalues::\n\n >>> b = Batch()\n >>> b.add_package(i)\n Traceback (most recent call last):\n ...\n OptionConflict: Total package weight must be specified when Customs.Items\n are used\n\n >>> b.add_package(i, WeightOz(1))\n Traceback (most recent call last):\n ...\n OptionConflict: Total item weight is 12 oz, but\n total package weight is only 1 oz\n\n >>> b.add_package(i, WeightOz(12), Value(69))\n Traceback (most recent call last):\n ...\n OptionConflict: Can't set 'Value=29.95' when 'Value=69' already set\n\nAnd a form type and contents type must be specified if you include any items::\n\n >>> b.add_package(i, WeightOz(12))\n Traceback (most recent call last):\n ...\n OptionConflict: Customs form + content type must be specified with items\n\n >>> b.add_package(i, WeightOz(12), Customs.Gift)\n Traceback (most recent call last):\n ...\n OptionConflict: Customs form + content type must be specified with items\n\n >>> b.add_package(i, WeightOz(12), Customs.CN22)\n Traceback (most recent call last):\n ...\n OptionConflict: Customs form + content type must be specified with items\n\n >>> b.add_package(i, WeightOz(12), Customs.Gift, Customs.CN22)\n >>> print b.tostring()\n \n \n 1\n United States\n Paperback book\n 12\n 29.95\n 12\n GIFT\n CN22\n 29.95\n \n \n\nThe final customs form will include the multiplied-out weights and values based\non the quantity of each item::\n\n >>> b = Batch()\n >>> b.add_package(\n ... Customs.Item('x',23,42,3), Customs.Item('y',1,7),\n ... WeightOz(99), Customs.Gift, Customs.CN22\n ... )\n >>> print b.tostring()\n \n \n 3\n United States\n x\n 69\n 126\n 1\n United States\n y\n 1\n 7\n 99\n GIFT\n CN22\n 133\n \n \n\n\nCustoms Signature\n-----------------\n\nYou can specify the person who's certifying the customs form using these\noptions::\n\n >>> Customs.Signer(\"Phillip Eby\")\n CustomsSigner('Phillip Eby')\n\n >>> Customs.Certify\n CustomsCertify('TRUE')\n\n\n\nProcessing Options\n==================\n\nXXX DAZzle.Test\nDAZzle.Layout(filename)\nDAZzle.OutputFile(filename)\nDAZzle.Print\nDAZzle.Verify\nDAZzle.SkipUnverified\nDAZzle.AutoClose\nDAZzle.Prompt\nDAZzle.AbortOnError\nDAZzle.AutoPrintCustomsForms\nDAZzle.XMLDirectory\nDAZzle.LayoutDirectory\nDAZzle.exe_path\n\n\nMiscellaneous\n=============\n\nXXX RubberStamp(n, text)\nReferenceID(text)\nCostCenter(int)\n\n\n\n-------------------\nInternals and Tests\n-------------------\n\nMisc imports for tests::\n\n >>> from pydicia import add_to_package, ET, Option, Batch, Package\n\nPackages::\n\n >>> b = Batch()\n >>> p = Package(b)\n\n >>> print b.tostring()\n \n \n \n\n >>> Box = Option('FlatRate', 'BOX')\n >>> add_to_package(Box, p, False)\n\n >>> print b.tostring()\n \n \n BOX\n \n \n\n >>> Envelope = Option('FlatRate', 'TRUE')\n >>> add_to_package(Envelope, p, False)\n Traceback (most recent call last):\n ...\n OptionConflict: Can't set 'FlatRate=TRUE' when 'FlatRate=BOX' already set\n\n >>> print b.tostring()\n \n \n BOX\n \n \n\n >>> add_to_package(Box, p, False)\n >>> print b.tostring()\n \n \n BOX\n \n \n\n >>> add_to_package(Envelope, p, True)\n >>> print b.tostring()\n \n \n BOX\n \n \n\n >>> del p.element[-1]; p.element.text=''\n >>> print b.tostring()\n \n \n \n\n >>> verify_zip = Option('DAZzle', 'DAZ', 'Start')\n\n >>> add_to_package(verify_zip, p, False)\n >>> print b.tostring()\n \n \n \n\n >>> add_to_package(Option('DAZzle', 'PRINTING', 'Start'), p, False)\n Traceback (most recent call last):\n ...\n OptionConflict: Can't set 'DAZzle.Start=PRINTING' when 'DAZzle.Start=DAZ' already set\n\n >>> b = Batch()\n >>> p = Package(b)\n >>> add_to_package([verify_zip, Envelope], p, False)\n >>> print b.tostring()\n \n \n TRUE\n \n \n\n >>> p.should_queue(Services.COD)\n True\n >>> print b.tostring()\n \n \n TRUE\n \n \n\n >>> p.finish()\n >>> print b.tostring()\n \n \n TRUE\n \n \n \n\n >>> p.should_queue(Services.COD)\n False\n\n\nBatch rollback::\n\n >>> b = Batch()\n >>> print b.tostring()\n \n\n >>> b.add_package(FlatRateEnvelope, FlatRateBox)\n Traceback (most recent call last):\n ...\n OptionConflict: Can't set 'PackageType=FLATRATEBOX' when\n 'PackageType=FLATRATEENVELOPE' already set\n\n >>> print b.tostring() # rollback on error\n \n\n\nMisc shipment and postprocessing::\n\n >>> s = Shipment(verify_zip)\n >>> s.add_package(Box)\n >>> s.add_package(Envelope)\n >>> root, = s.batches\n >>> print root.tostring()\n \n \n BOX\n \n \n TRUE\n \n \n\nOption inversion::\n\n >>> ~Envelope\n FlatRate('FALSE')\n >>> ~~Envelope\n FlatRate('TRUE')\n\n >>> ~Option('Services', 'ON', 'RegisteredMail')\n Services.RegisteredMail('OFF')\n >>> ~~Option('Services', 'ON', 'RegisteredMail')\n Services.RegisteredMail('ON')\n\n >>> ~Option('DAZzle', 'YES', 'Prompt')\n DAZzle.Prompt('NO')\n >>> ~~Option('DAZzle', 'YES', 'Prompt')\n DAZzle.Prompt('YES')\n\n\nThe ``iter_options()`` generic function yields \"option\" objects for an\napplication object. The default implementation is to raise an error::\n\n >>> from pydicia import iter_options\n\n >>> iter_options(27)\n Traceback (most recent call last):\n ...\n NotImplementedError: ('No option producer registered for', )\n\nAnd for lists and tuples, the default is to yield their contents::\n\n >>> list(iter_options((1, 2, 3)))\n [1, 2, 3]\n\n >>> list(iter_options(['a', 'b']))\n ['a', 'b']\n\nThis routine is used internally by ``add_to_package()``.", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://cheeseshop.python.org/pypi/PyDicia", "keywords": null, "license": "PSF or ZPL", "maintainer": null, "maintainer_email": null, "name": "PyDicia", "package_url": "https://pypi.org/project/PyDicia/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/PyDicia/", "project_urls": { "Download": "UNKNOWN", "Homepage": "http://cheeseshop.python.org/pypi/PyDicia" }, "release_url": "https://pypi.org/project/PyDicia/0.1a3/", "requires_dist": null, "requires_python": null, "summary": "Print labels, envelopes, US postage, and more using the endicia.com API", "version": "0.1a3" }, "last_serial": 785261, "releases": { "0.1a1": [ { "comment_text": "", "digests": { "md5": "aa403591994bb09cfeb30bd15200c2f0", "sha256": "2645b8d10be70f53cf107f2541ccf0d3e2eff64302ede76cabe0be87ae4ccb04" }, "downloads": -1, "filename": "PyDicia-0.1a1-py2.4.egg", "has_sig": false, "md5_digest": "aa403591994bb09cfeb30bd15200c2f0", "packagetype": "bdist_egg", "python_version": "2.4", "requires_python": null, "size": 33679, "upload_time": "2007-07-05T13:29:45", "url": "https://files.pythonhosted.org/packages/41/52/cdb8672510fecc0b682d86b21c6a332cb1226ebe168255d846d70c0254f5/PyDicia-0.1a1-py2.4.egg" }, { "comment_text": "", "digests": { "md5": "3dcb6125ef692fc52bd1b06b84b9e943", "sha256": "8e05a70355f608871f5f013f34f7d8960ecfd5b4378b6b5c7b46b29c1b89fd7d" }, "downloads": -1, "filename": "PyDicia-0.1a1-py2.5.egg", "has_sig": false, "md5_digest": "3dcb6125ef692fc52bd1b06b84b9e943", "packagetype": "bdist_egg", "python_version": "2.5", "requires_python": null, "size": 33281, "upload_time": "2007-07-05T13:30:01", "url": "https://files.pythonhosted.org/packages/5a/9a/fe6812b8bfbc9a3dcbae060a0acaaa2f4ca2550dc465f808765e1a742f80/PyDicia-0.1a1-py2.5.egg" }, { "comment_text": "", "digests": { "md5": "d7f74e380a04224e505049fefdc0862a", "sha256": "42b7282fe62b63534ae73e3a52b9c4318b1fcd776c9d14948fe7e3fe58103119" }, "downloads": -1, "filename": "PyDicia-0.1a1.zip", "has_sig": false, "md5_digest": "d7f74e380a04224e505049fefdc0862a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 36859, "upload_time": "2007-07-05T13:29:44", "url": "https://files.pythonhosted.org/packages/26/22/1cddcee599486f2115172d1dd448b75a36dca150793010588fbf6c6b22d6/PyDicia-0.1a1.zip" } ], "0.1a2": [ { "comment_text": "", "digests": { "md5": "b96318c7752b0caa1541d6634fd86ce6", "sha256": "95253282b740fa3b273ae5771f4148c65a9bc43bcfe50041a838a4eb076fc6fc" }, "downloads": -1, "filename": "PyDicia-0.1a2-py2.4.egg", "has_sig": false, "md5_digest": "b96318c7752b0caa1541d6634fd86ce6", "packagetype": "bdist_egg", "python_version": "2.4", "requires_python": null, "size": 34860, "upload_time": "2007-07-07T20:27:20", "url": "https://files.pythonhosted.org/packages/3f/bd/50644a2be41b803e4c810f5c560050ce6d3da61806a9d7cb26d62d228181/PyDicia-0.1a2-py2.4.egg" }, { "comment_text": "", "digests": { "md5": "cdf5a9ea66adfebfc68bfcba2c8ce49a", "sha256": "1df46c84148dfc7360f52acbe463866f4fa7c2dfd2c69f65814e451147b5b0c3" }, "downloads": -1, "filename": "PyDicia-0.1a2-py2.5.egg", "has_sig": false, "md5_digest": "cdf5a9ea66adfebfc68bfcba2c8ce49a", "packagetype": "bdist_egg", "python_version": "2.5", "requires_python": null, "size": 34422, "upload_time": "2007-07-07T20:27:54", "url": "https://files.pythonhosted.org/packages/84/47/8fc6df657c3d0cb064c2576ed5d1d402458861e7961cef7694533ab2da16/PyDicia-0.1a2-py2.5.egg" }, { "comment_text": "", "digests": { "md5": "5a0ecb47e93baa69b939b62e54d71299", "sha256": "a1d36ad642854322c6c17a9512d054d4e1be801588c563ad29128e0c7c11271b" }, "downloads": -1, "filename": "PyDicia-0.1a2.zip", "has_sig": false, "md5_digest": "5a0ecb47e93baa69b939b62e54d71299", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 39467, "upload_time": "2007-07-07T20:27:18", "url": "https://files.pythonhosted.org/packages/a2/7a/e1b986c5aa7ea593d507feb4819195a4ad19a5b94f0445a53eb11b54e008/PyDicia-0.1a2.zip" } ], "0.1a3": [ { "comment_text": "", "digests": { "md5": "846a4e1b554deddbc11624d294f6eac3", "sha256": "160a9e7fb7405ea6642dce62cfc685414250b4e22b40c2de7368667a1672db39" }, "downloads": -1, "filename": "PyDicia-0.1a3-py2.4.egg", "has_sig": false, "md5_digest": "846a4e1b554deddbc11624d294f6eac3", "packagetype": "bdist_egg", "python_version": "2.4", "requires_python": null, "size": 35023, "upload_time": "2008-03-05T16:54:24", "url": "https://files.pythonhosted.org/packages/06/33/8a7c4600ccbab2a1c7bfc45095e31f13c3afb71b689b57fa9193c5bad40b/PyDicia-0.1a3-py2.4.egg" }, { "comment_text": "", "digests": { "md5": "7bb08c19a2cb756b0f52cb35b1a1e621", "sha256": "415bf2fe70406e3234bda3259bb231de27f193d5837da791bd47bb669bbeb1be" }, "downloads": -1, "filename": "PyDicia-0.1a3-py2.5.egg", "has_sig": false, "md5_digest": "7bb08c19a2cb756b0f52cb35b1a1e621", "packagetype": "bdist_egg", "python_version": "2.5", "requires_python": null, "size": 34607, "upload_time": "2008-03-05T16:53:07", "url": "https://files.pythonhosted.org/packages/02/34/23b7471de2ce7c95c26e1b753ea0994b808d13bf06de01a5aeabae197048/PyDicia-0.1a3-py2.5.egg" }, { "comment_text": "", "digests": { "md5": "c77e56e8b341ba8ccd7bbbaf63afcda6", "sha256": "193da2ece127f863b05e1c76747c383787cdf13d2394831f0c864768e08431ac" }, "downloads": -1, "filename": "PyDicia-0.1a3.zip", "has_sig": false, "md5_digest": "c77e56e8b341ba8ccd7bbbaf63afcda6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 39737, "upload_time": "2008-03-05T16:53:05", "url": "https://files.pythonhosted.org/packages/4d/57/2e65c97b001401379ba3d913a8294425e98161a6212093c7278f79b86811/PyDicia-0.1a3.zip" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "846a4e1b554deddbc11624d294f6eac3", "sha256": "160a9e7fb7405ea6642dce62cfc685414250b4e22b40c2de7368667a1672db39" }, "downloads": -1, "filename": "PyDicia-0.1a3-py2.4.egg", "has_sig": false, "md5_digest": "846a4e1b554deddbc11624d294f6eac3", "packagetype": "bdist_egg", "python_version": "2.4", "requires_python": null, "size": 35023, "upload_time": "2008-03-05T16:54:24", "url": "https://files.pythonhosted.org/packages/06/33/8a7c4600ccbab2a1c7bfc45095e31f13c3afb71b689b57fa9193c5bad40b/PyDicia-0.1a3-py2.4.egg" }, { "comment_text": "", "digests": { "md5": "7bb08c19a2cb756b0f52cb35b1a1e621", "sha256": "415bf2fe70406e3234bda3259bb231de27f193d5837da791bd47bb669bbeb1be" }, "downloads": -1, "filename": "PyDicia-0.1a3-py2.5.egg", "has_sig": false, "md5_digest": "7bb08c19a2cb756b0f52cb35b1a1e621", "packagetype": "bdist_egg", "python_version": "2.5", "requires_python": null, "size": 34607, "upload_time": "2008-03-05T16:53:07", "url": "https://files.pythonhosted.org/packages/02/34/23b7471de2ce7c95c26e1b753ea0994b808d13bf06de01a5aeabae197048/PyDicia-0.1a3-py2.5.egg" }, { "comment_text": "", "digests": { "md5": "c77e56e8b341ba8ccd7bbbaf63afcda6", "sha256": "193da2ece127f863b05e1c76747c383787cdf13d2394831f0c864768e08431ac" }, "downloads": -1, "filename": "PyDicia-0.1a3.zip", "has_sig": false, "md5_digest": "c77e56e8b341ba8ccd7bbbaf63afcda6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 39737, "upload_time": "2008-03-05T16:53:05", "url": "https://files.pythonhosted.org/packages/4d/57/2e65c97b001401379ba3d913a8294425e98161a6212093c7278f79b86811/PyDicia-0.1a3.zip" } ] }