[Subversion] / PyDicia / README.txt  

Diff of /PyDicia/README.txt

Parent Directory | Revision Log

version 2321, Wed Jul 4 17:13:50 2007 UTC version 2331, Sat Jul 7 20:20:50 2007 UTC
Line 13 
Line 13 
 "packing slip" objects) without subclassing.  (This is particularly useful if  "packing slip" objects) without subclassing.  (This is particularly useful if
 you are extending a CRM or other database that was written by somebody else.)  you are extending a CRM or other database that was written by somebody else.)
   
   This version of PyDicia is an alpha proof-of-concept release.  It is actually
   usable -- I've already used it to print about a dozen international shipping
   labels to almost as many countries.  However, the API is subject to change,
   the reference documentation is sketchy, and the developer's guide lacks detail
   about some of the more advanced features.  This should improve in future
   releases, but I just want to get this milestone out to start with.  Reading the
   DAZzle XML API specification is a good idea if you want to use this; make sure
   you get the 7.0.x version, as that's required.
   
 PyDicia uses the ElementTree, simplegeneric, and DecoratorTools packages, and  PyDicia uses the ElementTree, simplegeneric, and DecoratorTools packages, and
 requires Python 2.4 or higher (due to use of decorators and the ``Decimal``  requires Python 2.4 or higher (due to use of decorators and the ``Decimal``
 type).  type).  Actually printing any labels requires that you have an Endicia
   "Premium" or "Mac" account.  (Note: I have not used the Mac client, so I don't
   know how well it works there.  See the section below on `Using PyDicia on
   Non-Windows Platforms`_ for more info.)
   
   Questions, discussion, and bug reports for this software should be directed to
   the PEAK mailing list; see http://www.eby-sarna.com/mailman/listinfo/PEAK/
   for details.
   
 IMPORTANT  IMPORTANT
     Please note that PyDicia does not attempt to implement all of the US Postal      Please note that PyDicia does not attempt to implement all of the US Postal
Line 32 
Line 48 
     been warned!      been warned!
   
   
 TODO:  .. contents:: **Table of Contents**
   
 * Cmd-line and queue mode handlers  
   
 * Response parsing and application  
   
   
 -----------------  -----------------
Line 85 
Line 97 
   
 The ``add_package()`` method accepts zero or more objects that can manipulate  The ``add_package()`` method accepts zero or more objects that can manipulate
 PyDicia package objects.  It also accepts tuples or lists of such objects,  PyDicia package objects.  It also accepts tuples or lists of such objects,
 nested to arbitrary depth.  nested to arbitrary depth::
   
     >>> b.add_package([COD, (Stealth, ToName('Ty Sarna'))], FlatRateBox)      >>> b.add_package([Services.COD, (Stealth, ToName('Ty Sarna'))], FlatRateBox)
   
     >>> print b.tostring()      >>> print b.tostring()
     <DAZzle>      <DAZzle>
Line 106 
Line 118 
 have been passed to ``add_package()``::  have been passed to ``add_package()``::
   
     >>> b.packages      >>> b.packages
     [(Option('ToName', 'Phillip Eby', None),),      [Package(ToName('Phillip Eby'),),
      ([Option('Services', 'ON', 'COD'), (Option('Stealth', 'TRUE', None),       Package([Services.COD('ON'), (Stealth('TRUE'), ToName('Ty Sarna'))],
        Option('ToName', 'Ty Sarna', None))],        PackageType('FLATRATEBOX'))]
       Option('PackageType', 'FLATRATEBOX', None))]  
   Each package object in the list wraps a tuple of the arguments that were
 Each "package" in the list is a tuple of the arguments that were supplied for  supplied for each invocation of ``add_package()``.  This allows the system
 each invocation of ``add_package()``.  to send status updates (including delivery confirmation numbers, customs IDs,
   etc.) back to the application.
   
 Treating Your Objects as Packages  But before we can process status updates, we need to have some application
 ---------------------------------  objects, as described in the next section.
   
 It also accepts any custom objects of your own design, that are registered with  
 the ``pydicia.add_to_package()`` or ``pydicia.iter_options()`` generic  Using Your Application Objects as Package Sources
 functions::  -------------------------------------------------
   
   In addition to PyDicia-defined objects and sequences thereof, the
   ``add_package()`` method also accepts any custom objects of your own design
   that have been registered with the ``pydicia.add_to_package()`` or
   ``pydicia.iter_options()`` generic functions::
   
     >>> class Customer:      >>> class Customer:
     ...     def __init__(self, **kw):      ...     def __init__(self, **kw):
Line 185 
Line 202 
         </Package>          </Package>
     </DAZzle>      </DAZzle>
   
 Also note that there is no particular significance to my choice of lists vs.  Note that there is no particular significance to my choice of lists vs. tuples
 tuples in these examples; they're more to demonstrate that you can use  in these examples; they're more to demonstrate that you can use arbitrary
 arbitrary structures, as long as they contain objects that are supported by  structures, as long as they contain objects that are supported by either
 either ``iter_options()`` or ``add_to_package()``.  Normally, you will simply  ``iter_options()`` or ``add_to_package()``.  Normally, you will simply use
 use collections of either PyDicia-provided symbols, or application objects for  collections of either PyDicia-provided symbols, or application objects for
 which you've defined an ``iter_options()`` method.  which you've defined an ``iter_options()`` method.
   
 You will also usually want to implement your PyDicia support in a module by  You will also usually want to implement your PyDicia support in a module by
Line 219 
Line 236 
 Multi-Batch Shipments  Multi-Batch Shipments
 =====================  =====================
   
 Certain DAZzle options can only be set once per file, such as the choice of  Certain DAZzle options can only be set once per XML file, such as the choice of
 layout file.  If you are shipping multiple packages with different label  layout file.  If you are shipping multiple packages with different label
 layouts (such as domestic vs. international mail), you need to separate these  layouts (such as domestic vs. international mail), you need to separate these
 packages into different batches.  The ``Shipment`` class handles this  packages into different batches, each of which will be in a separate XML file.
 separation for you automatically.  The ``Shipment`` class handles this separation for you automatically.
   
 When you create a shipment, it initially has no batches::  When you create a shipment, it initially has no batches::
   
Line 234 
Line 251 
   
 But as you add packages to it, it will create batches as needed::  But as you add packages to it, it will create batches as needed::
   
     >>> s.add_package(ToName('Phillip Eby'), Test)      >>> s.add_package(ToName('Phillip Eby'), DAZzle.Test)
     >>> len(s.batches)      >>> len(s.batches)
     1      1
   
Line 248 
Line 265 
 As long as you're adding packages with the same or compatible options, the  As long as you're adding packages with the same or compatible options, the
 same batch will be reused::  same batch will be reused::
   
     >>> s.add_package(ToName('Ty Sarna'), Test)      >>> s.add_package(ToName('Ty Sarna'), DAZzle.Test)
     >>> len(s.batches)      >>> len(s.batches)
     1      1
     >>> print s.batches[0].tostring()      >>> print s.batches[0].tostring()
Line 264 
Line 281 
 But as soon as you add a package with any incompatible options, a new batch  But as soon as you add a package with any incompatible options, a new batch
 will be created and used::  will be created and used::
   
     >>> s.add_package(ToName('PJE'), ~Test)      >>> s.add_package(ToName('PJE'), ~DAZzle.Test)
     >>> len(s.batches)      >>> len(s.batches)
     2      2
   
Line 277 
Line 294 
   
 And each time you add a package, it's added to the first compatible batch::  And each time you add a package, it's added to the first compatible batch::
   
     >>> s.add_package(ToName('Some Body'), ~Test)      >>> s.add_package(ToName('Some Body'), ~DAZzle.Test)
     >>> len(s.batches)      >>> len(s.batches)
     2      2
   
Line 291 
Line 308 
         </Package>          </Package>
     </DAZzle>      </DAZzle>
   
     >>> s.add_package(ToName('No Body'), Test)      >>> s.add_package(ToName('No Body'), DAZzle.Test)
     >>> len(s.batches)      >>> len(s.batches)
     2      2
   
Line 311 
Line 328 
 By the way, as with batches, you can create a shipment with options that will  By the way, as with batches, you can create a shipment with options that will
 be applied to all packages::  be applied to all packages::
   
     >>> s = Shipment(Tomorrow, COD)      >>> s = Shipment(Tomorrow, Services.COD)
     >>> s.add_package(ToName('Some Body'), Test)      >>> s.add_package(ToName('Some Body'), DAZzle.Test)
     >>> s.add_package(ToName('No Body'), ~Test)      >>> s.add_package(ToName('No Body'), ~DAZzle.Test)
     >>> len(s.batches)      >>> len(s.batches)
     2      2
     >>> print s.batches[0].tostring()      >>> print s.batches[0].tostring()
Line 336 
Line 353 
   
   
   
 Invoking DAZzle  Receiving Status Updates
 ===============  ========================
   
   When DAZzle completes a batch, it creates an output file containing status
   information for each package in the batch.  If you'd like to process this
   status information for the corresponding application objects you passed in
   to ``add_package()``, you can extend the ``report_status()`` generic function
   to do this::
   
       >>> @report_status.when_type(Customer)
       ... def customer_status(ob, status):
       ...     print ob
       ...     print status
   
       >>> b = Batch()
       >>> b.add_package(c)
   
   When the batch receives status information, it will invoke ``report_status()``
   on each package's application items, with a status object for the corresponding
   package::
   
       >>> b.report_statuses()
       <...Customer instance...>
       ToAddress           : [u'123 Nowhere Dr']
       ToCity              : u'Nowhere'
       ToState             : u'FL'
       ToPostalCode        : u'12345-6789'
       ToAddress1          : u'123 Nowhere Dr'
   
   Note that you don't normally need to call ``report_statuses()`` directly; it's
   usually done for you as part of the process of running a batch or shipment.
   (See the section below on `Invoking DAZzle`_.)
   
   The `status` object passed to your method will be a ``Status`` instance with
   attributes similar to those above, containing USPS-normalized address data.
   In addition, several other fields are possible::
   
       >>> from pydicia import ET
       >>> b.etree = ET.fromstring('''
       ... <DAZzle><Package ID="1">
       ...     <ToZip4>1234</ToZip4>
       ...     <Status>Rejected (-3)</Status>
       ...     <PIC>123465874359</PIC>
       ...     <FinalPostage>4.60</FinalPostage>
       ...     <TransactionDateTime>20070704173221</TransactionDateTime>
       ...     <PostmarkDate>20070705</PostmarkDate>
       ... </Package></DAZzle>''')
   
       >>> b.report_statuses()
       <...Customer instance...>
       Status              : 'Rejected (-3)'
       ErrorCode           : -3
       ToAddress           : []
       ToZip4              : '1234'
       PIC                 : '123465874359'
       FinalPostage        : Decimal("4.60")
       TransactionDateTime : datetime.datetime(2007, 7, 4, 17, 32, 21)
       PostmarkDate        : datetime.date(2007, 7, 5)
   
   The ``Status`` object should support all output fields supported by DAZzle; see
   the DAZzle documentation for details.  The non-string fields shown above are
   the only ones which are postprocessed to specialized Python objects; the rest
   are kept as strings or Unicode values.  The ``ErrorCode`` field is computed by
   extracting the integer portion of any rejection code.  It is ``None`` in the
   case of a successful live print, and ``0`` in the case of a successful test
   print.  See the DAZzle XML interface documentation for a description of other
   error codes.
   
   Note that for a more compact presentation, attributes with ``None`` values are
   not included in the ``str()`` of a ``Status`` object, which is why the statuses
   displayed above show different sets of fields.  The attributes, however, always
   exist; they simply have ``None`` as their value.
   
 XXX  
   
   Invoking DAZzle
   ===============
   
 Application Integration  In the simplest case, invoking a batch or shipment objects ``.run()`` method
 =======================  will launch a local copy of DAZzle on a temporary file containing the batch's
   XML, wait for DAZzle to exit, then process status updates from the output file
   and return DAZzle's return code.  (Or a list of return codes, in the case of a
   ``Shipment``.)
   
   If you are using this approach, you may wish to include ``~DAZzle.Prompt``
   (which keeps end-user prompts to a minimum) and ``DAZzle.AutoClose`` (so that
   DAZzle exits upon completion of the batch) in your batch options.
   
   If you do not have a local copy of DAZzle, but instead are using a network
   queue directory to send jobs remotely, you can instead use the batch object's
   ``.write(queuedir)`` method to send the batch to the queue.  You can also
   use this approach to send jobs to a local copy of DAZzle running in the
   background.
   
   If a copy of DAZzle is installed locally, you can get its XML queue directory
   from ``DAZzle.XMLDirectory``, and check whether it is monitoring for files
   using ``DAZzle.get_preference("MonitorXML")``.  (These values will be ``None``
   if DAZzle is not installed.)
   
   If DAZzle is installed locally, you can launch it with the
   ``DAZzle.run(args=(), sync=True)`` function.  The `args` are a list of command
   line arguments to pass, and `sync` is a flag indicating whether to wait for
   DAZzle to exit.  If `sync` is a false value, ``run()`` returns a
   ``subprocess.Popen`` instance.  Otherwise, it returns the process's exit code
   (ala ``subprocess.call``).
   
   XXX async batch status retrieval
   
   XXX DAZzle.exe_path, DAZzle.get_preference(), DAZzle.LayoutDirectory
   
   XXX Launching for multi-batch, remote, queued, and other async processing
   
   
   Using PyDicia on Non-Windows Platforms
   ======================================
   
   When used on a non-Windows platform, PyDicia cannot detect any DAZzle
   configuration information, so you must manually set ``DAZzle.exe_path`` to
   the client program, if you wish to use any of the ``run()`` methods.
   (Likewise, you must manually set ``DAZzle.LayoutDirectory`` if you want layout
   paths to be automatically adjusted.)
   
   On the Mac, the ``exe_path`` should be set to a program that takes a single
   XML filename as an argument.  The Mac ``endiciatool`` program probably will
   not work on its own, without a wrapper shell script of some kind; I'm open to
   suggestions as to how to improve this.  Note, by the way, that the Mac client
   doesn't support all of the options that the Windows client does, so remember
   that use of PyDicia is entirely at your own risk, whatever the platform!
   
   On other platforms, the main usefulness of PyDicia would be in generating XML
   for users to download (e.g. from a web application) or submitting and
   processing jobs via a Samba-mounted queue directory.  You don't need an
   ``exe_path`` for this, but you will need to generate your own layout and output
   file paths using ``Option`` objects, to avoid them being mangled by PyDicia's
   platform-specific path munging.
   
 Status handling, Address updating, ToReturnCode()...  XXX explain how to do that, or make it work anyway
   
   
 Advanced Customization  Advanced Customization
 ======================  ======================
   
 Using Option elements, add_to_package()  XXX Using Option elements, add_to_package()
   
   
   
 -----------------  -----------------
Line 362 
Line 504 
 Basic Package Options  Basic Package Options
 =====================  =====================
   
 MailClass(text), NoPostage  XXX MailClass(text), NoPostage
   
 DateAdvance(), Today, Tomorrow  DateAdvance(), Today, Tomorrow
 Value()  Value()
 Description()  Description()
Line 373 
Line 514 
 Addresses  Addresses
 =========  =========
   
   ::
     >>> ToName("Phillip J. Eby")      >>> ToName("Phillip J. Eby")
     Option('ToName', 'Phillip J. Eby', None)      ToName('Phillip J. Eby')
   
     >>> ToTitle("President")      >>> ToTitle("President")
     Option('ToTitle', 'President', None)      ToTitle('President')
   
     >>> ToCompany("Dirt Simple, Inc.")      >>> ToCompany("Dirt Simple, Inc.")
     Option('ToCompany', 'Dirt Simple, Inc.', None)      ToCompany('Dirt Simple, Inc.')
   
   
 ToAddress(*lines)  XXX ToAddress(\*lines)
 ToCity(text), ToState(text), ToPostalCode(text), ToZIP4(text), ToCountry(text)  ToCity(text), ToState(text), ToPostalCode(text), ToZIP4(text), ToCountry(text)
   
 ReturnAddress(*lines)  XXX ReturnAddress(\*lines)
   
 ToDeliveryPoint(text)  ToDeliveryPoint(text)
 EndorsementLine(text)  EndorsementLine(text)
 ToCarrierRoute(text)  ToCarrierRoute(text)
Line 396 
Line 537 
 Package Details  Package Details
 ===============  ===============
   
 PackageType()  XXX PackageType()
 FlatRateEnvelope  FlatRateEnvelope
 FlatRateBox  FlatRateBox
 RectangularParcel  RectangularParcel
Line 404 
Line 545 
 Postcard  Postcard
 Flat  Flat
 Envelope  Envelope
   
 Width(), Length(), Depth()  Width(), Length(), Depth()
   
 NonMachinable  NonMachinable
 BalloonRate  BalloonRate
   
   
   
 Service Options  Service Options
 ===============  ===============
   
 ReplyPostage  XXX ReplyPostage
 Stealth  Stealth
   
 SignatureWaiver  SignatureWaiver
 NoWeekendDelivery  NoWeekendDelivery
 NoHolidayDelivery  NoHolidayDelivery
 ReturnToSender  ReturnToSender
   
 Insurance.USPS  Insurance.USPS
 Insurance.Endicia  Insurance.Endicia
 Insurance.UPIC  Insurance.UPIC
 Insurance.NONE  Insurance.NONE
   Services.RegisteredMail
 RegisteredMail  Services.CertifiedMail
 CertifiedMail  Services.RestrictedDelivery
 RestrictedDelivery  Services.CertificateOfMailing
 CertificateOfMailing  Services.ReturnReceipt
 ReturnReceipt  Services.DeliveryConfirmation
 DeliveryConfirmation  Services.SignatureConfirmation
 SignatureConfirmation  Services.COD
 COD  Services.InsuredMail()
   
   
 Customs Forms  Customs Forms
 =============  =============
   
 Customs.Sample  When processing international shipments, you will usually need to specify a
 Customs.Gift  customs form, contents type, and items.  Additionally, if you want to print
 Customs.Documents  the customs forms already "signed", you can specify a signer and the
 Customs.Other  certification option.
 Customs.Merchandise  
   Contents Types
 Customs.GEM  --------------
 Customs.CN22  
 Customs.CP72  The ``ContentsType`` constructor defines the type of contents declared on the
 Customs.NONE  customs form.  There are six predefined constants for the standard contents
   types::
   
       >>> Customs.Sample
       ContentsType('SAMPLE')
   
       >>> Customs.Gift
       ContentsType('GIFT')
   
       >>> Customs.Documents
       ContentsType('DOCUMENTS')
   
       >>> Customs.Other
       ContentsType('OTHER')
   
       >>> Customs.Merchandise
       ContentsType('MERCHANDISE')
   
       >>> Customs.ReturnedGoods
       ContentsType('RETURNEDGOODS')
   
 Customs.Item(desc, weight, value, qty=1, origin='United States')  
   
 Customs.Signer(text)  Customs Form Types
 Customs.Certify  ------------------
   
   The ``CustomsFormType`` constructor defines the type of customs form to be
   used.  There are four predefined constants for the allowed form types::
   
       >>> Customs.GEM
       CustomsFormType('GEM')
   
       >>> Customs.CN22
       CustomsFormType('CN22')
   
       >>> Customs.CP72
       CustomsFormType('CP72')
   
       >>> Customs.NONE
       CustomsFormType('NONE')
   
   
   Customs Items
   -------------
   
   Items to be declared on a customs form are created using ``Customs.Item``.
   The minimum required arguments are a description, a unit weight in ounces
   (which must be an integer or decimal), and a value in US dollars (also an
   integer or decimal)::
   
       >>> from decimal import Decimal
       >>> i = Customs.Item("Paperback book", 12, Decimal('29.95'))
   
   You may also optionally specify a quantity (which must be an integer) and a
   country of origin.  The defaults for these are ``1`` and ``"United States"``,
   respectively::
   
       >>> i
       Item('Paperback book', Decimal("12"), Decimal("29.95"), 1, 'United States')
   
   You always specify a unit weight and value; these are automatically multiplied
   by the quantity on the customs form, and for purposes of calculating total
   weight/value.
   
   Note that a package's total weight must be greater than or equal to the sum of
   its items' weight, and its value must exactly equal the sum of its items'
   values::
   
       >>> b = Batch()
       >>> b.add_package(i)
       Traceback (most recent call last):
         ...
       OptionConflict: Total package weight must be specified when Customs.Items
                       are used
   
       >>> b.add_package(i, WeightOz(1))
       Traceback (most recent call last):
         ...
       OptionConflict: Total item weight is 12 oz, but
                       total package weight is only 1 oz
   
       >>> b.add_package(i, WeightOz(12), Value(69))
       Traceback (most recent call last):
         ...
       OptionConflict: Can't set 'Value=29.95' when 'Value=69' already set
   
   And a form type and contents type must be specified if you include any items::
   
       >>> b.add_package(i, WeightOz(12))
       Traceback (most recent call last):
         ...
       OptionConflict: Customs form + content type must be specified with items
   
       >>> b.add_package(i, WeightOz(12), Customs.Gift)
       Traceback (most recent call last):
         ...
       OptionConflict: Customs form + content type must be specified with items
   
       >>> b.add_package(i, WeightOz(12), Customs.CN22)
       Traceback (most recent call last):
         ...
       OptionConflict: Customs form + content type must be specified with items
   
       >>> b.add_package(i, WeightOz(12), Customs.Gift, Customs.CN22)
       >>> print b.tostring()
       <DAZzle>
           <Package ID="1">
               <CustomsQuantity1>1</CustomsQuantity1>
               <CustomsCountry1>United States</CustomsCountry1>
               <CustomsDescription1>Paperback book</CustomsDescription1>
               <CustomsWeight1>12</CustomsWeight1>
               <CustomsValue1>29.95</CustomsValue1>
               <WeightOz>12</WeightOz>
               <ContentsType>GIFT</ContentsType>
               <CustomsFormType>CN22</CustomsFormType>
               <Value>29.95</Value>
           </Package>
       </DAZzle>
   
   The final customs form will include the multiplied-out weights and values based
   on the quantity of each item::
   
       >>> b = Batch()
       >>> b.add_package(
       ...     Customs.Item('x',23,42,3), Customs.Item('y',1,7),
       ...     WeightOz(99), Customs.Gift, Customs.CN22
       ... )
       >>> print b.tostring()
       <DAZzle>
           <Package ID="1">
               <CustomsQuantity1>3</CustomsQuantity1>
               <CustomsCountry1>United States</CustomsCountry1>
               <CustomsDescription1>x</CustomsDescription1>
               <CustomsWeight1>69</CustomsWeight1>
               <CustomsValue1>126</CustomsValue1>
               <CustomsQuantity2>1</CustomsQuantity2>
               <CustomsCountry2>United States</CustomsCountry2>
               <CustomsDescription2>y</CustomsDescription2>
               <CustomsWeight2>1</CustomsWeight2>
               <CustomsValue2>7</CustomsValue2>
               <WeightOz>99</WeightOz>
               <ContentsType>GIFT</ContentsType>
               <CustomsFormType>CN22</CustomsFormType>
               <Value>133</Value>
           </Package>
       </DAZzle>
   
   
   Customs Signature
   -----------------
   
   You can specify the person who's certifying the customs form using these
   options::
   
       >>> Customs.Signer("Phillip Eby")
       CustomsSigner('Phillip Eby')
   
       >>> Customs.Certify
       CustomsCertify('TRUE')
   
 ContentsType(), CustomsFormType()  
   
   
 Processing Options  Processing Options
 ==================  ==================
   
 Test  XXX DAZzle.Test
 Layout(filename)  DAZzle.Layout(filename)
 OutputFile(filename)  DAZzle.OutputFile(filename)
   DAZzle.Print
 Print  DAZzle.Verify
 Verify  DAZzle.SkipUnverified
   DAZzle.AutoClose
 SkipUnverified  DAZzle.Prompt
 AutoClose  DAZzle.AbortOnError
 Prompt  DAZzle.AutoPrintCustomsForms
 AbortOnError  DAZzle.XMLDirectory
 AutoPrintCustomsForms  DAZzle.LayoutDirectory
   DAZzle.exe_path
   
   
 Miscellaneous  Miscellaneous
 =============  =============
   
 RubberStamp(n, text)  XXX RubberStamp(n, text)
 ReferenceID(text)  ReferenceID(text)
 CostCenter(int)  CostCenter(int)
   
Line 490 
Line 778 
 Internals and Tests  Internals and Tests
 -------------------  -------------------
   
   Misc imports for tests::
   
     >>> from pydicia import add_to_package, ET, Option, Batch, Package      >>> from pydicia import add_to_package, ET, Option, Batch, Package
   
 Packages::  Packages::
Line 570 
Line 860 
         </Package>          </Package>
     </DAZzle>      </DAZzle>
   
     >>> p.should_queue(COD)      >>> p.should_queue(Services.COD)
     True      True
     >>> print b.tostring()      >>> print b.tostring()
     <DAZzle Start="DAZ">      <DAZzle Start="DAZ">
Line 588 
Line 878 
         </Package>          </Package>
     </DAZzle>      </DAZzle>
   
     >>> p.should_queue(COD)      >>> p.should_queue(Services.COD)
     False      False
   
   
Line 608 
Line 898 
     <DAZzle />      <DAZzle />
   
   
 Misc shipment::  Misc shipment and postprocessing::
   
     >>> s = Shipment(verify_zip)      >>> s = Shipment(verify_zip)
     >>> s.add_package(Box)      >>> s.add_package(Box)
Line 624 
Line 914 
         </Package>          </Package>
     </DAZzle>      </DAZzle>
   
   
 Option inversion::  Option inversion::
   
     >>> ~Envelope      >>> ~Envelope
     Option('FlatRate', 'FALSE', None)      FlatRate('FALSE')
     >>> ~~Envelope      >>> ~~Envelope
     Option('FlatRate', 'TRUE', None)      FlatRate('TRUE')
   
     >>> ~Option('Services', 'ON', 'RegisteredMail')      >>> ~Option('Services', 'ON', 'RegisteredMail')
     Option('Services', 'OFF', 'RegisteredMail')      Services.RegisteredMail('OFF')
     >>> ~~Option('Services', 'ON', 'RegisteredMail')      >>> ~~Option('Services', 'ON', 'RegisteredMail')
     Option('Services', 'ON', 'RegisteredMail')      Services.RegisteredMail('ON')
   
     >>> ~Option('DAZzle', 'YES', 'Prompt')      >>> ~Option('DAZzle', 'YES', 'Prompt')
     Option('DAZzle', 'NO', 'Prompt')      DAZzle.Prompt('NO')
     >>> ~~Option('DAZzle', 'YES', 'Prompt')      >>> ~~Option('DAZzle', 'YES', 'Prompt')
     Option('DAZzle', 'YES', 'Prompt')      DAZzle.Prompt('YES')
   
   
 The ``iter_options()`` generic function yields "option" objects for an  The ``iter_options()`` generic function yields "option" objects for an


Generate output suitable for use with a patch program
Legend:
Removed from v.2321  
changed lines
  Added in v.2331

cvs-admin@eby-sarna.com

Powered by ViewCVS 1.0-dev

ViewCVS and CVS Help