Daniel Nouri's Old Blog

Note that this blog is discontinued. You can find my new blog here: Daniel Nouri's Blog.

Thu, 10 Aug 2006

Uploading a setuptools package to PyPI a.k.a. the CheeseShop

I'm writing this down because it's annoying me to death: Everytime I do this, I need to learn how to again.

So, Daniel, after you created a tag, get rid of the setup.cfg file that's there and commit. Then register the metadata for this package by running python setup.py register. After that, upload the package: python setup.py sdist bdist_egg upload -s (-s stands for sign). Upload other distribution formats the same way.

If you get this incredibly unhelpful error message, get yourself a pypirc file:

Submitting dist/your.egg to http://www.python.org/pypi
Upload failed (401): Authorization Required

The CheeseShop tutorial has useful pointers.

And next, be annoyed with doing this work again for getting your package into Plone.org's Products. (Sadly, the PloneSoftwareCenter developers decided not to support the PyPI API.)

posted at: 11:03 | 2 comments | category: /devel rss | permanent link | add to del.icio.us or digg it

Tue, 01 Aug 2006

Mocky: A mocking test class

Yesterday, I got tired with building mock objects for tests. Then I had this idea of creating a universal mock object that would help me with lots of my mocking needs: Mocky was born.

I haven't looked into other mock object libraries, but please feel free and spoil the party by sending me a link to some library where this has been implemented ages ago.

OK, enough words, time for you to take a look at the source (don't be scared, most of it is documentation):

class Mocky(object):
    """Mocky is a class that wants to help you with setting up mock
    objects for your tests.  It helps you observe which functions get
    called (with which parameters) and which attributes are set.

    Unless given a name, a Mocky's name is 'root':

      >>> Mocky().name
      'root'

    Let's start with a simple example that sets some variables so we
    get a feeling of how Mocky works.  Note that attribute accesss
    will never result in AttributeError.  Instead, an attribute access
    to a nonexistent member variable will yield another Mocky
    instance:

      >>> f = Mocky('f')
      >>> f
      f
      >>> unusual = f.unusual
      >>> unusual
      f.unusual
      >>> type(unusual) is Mocky
      True
      >>> unusual is f.unusual
      True
      >>> f.a.c.r = 'Fidelio'
      Set f.a.c.r to 'Fidelio'
      >>> f.a.c.r
      'Fidelio'

    Note that when we set 'f.a.c.r' to 'Fidelio', Mocky printed out
    that the attribute was set.  Suppose we have a function 'fun' that
    sets some fancy variable on a given object:

      >>> def fun(obj):
      ...     if obj.please_process_me:
      ...         obj.there_you = 'go'
      >>> myobj = Mocky('myobj')
      >>> fun(myobj)
      Set myobj.there_you to 'go'
      >>> myobj.please_process_me = False
      Set myobj.please_process_me to False
      >>> fun(myobj)

    Mocky also supports calling.  Another function that does a bit
    more with our test object:

      >>> def starve(character):
      ...     character.getStatus().hitpoints -= 1
      >>> starve(Mocky('Hugo')) # doctest: +ELLIPSIS
      Traceback (most recent call last):
      ...
      TypeError: unsupported operand type(s) for -=: 'Mocky' and 'int'
      >>> ezequiel = Mocky('ezequiel')
      >>> ezequiel.getStatus().hitpoints = 0
      Called ezequiel.getStatus()
      Set ezequiel.getStatus().hitpoints to 0
      >>> starve(ezequiel)
      Called ezequiel.getStatus()
      Set ezequiel.getStatus().hitpoints to -1

    For calls, Mocky will return the same value if the signature is
    the same:

      >>> secret = f.unusual(password='secret')
      Called f.unusual(password='secret')
      >>> secret is f.unusual(password='secret')
      Called f.unusual(password='secret')
      True
      >>> secret is f.unusual(password='unsafe')
      Called f.unusual(password='unsafe')
      False
    """
    def __init__(self, name='root'):
        self.__dict__['name'] = name
        self.__dict__['_calls'] = {}

    def __call__(self, *args, **kwargs):
        argsstr = ', '.join([repr(arg) for arg in args])
        keys = sorted(kwargs.keys())
        kwargsstr = ', '.join(['%s=%r' % (key, kwargs[key]) for key in keys])
        if argsstr and kwargsstr:
            allargs = ', '.join([argsstr, kwargsstr])
        else:
            allargs = argsstr or kwargsstr

        print "Called %s(%s)" % (self.name, allargs)
        if allargs not in self._calls:
            self._calls[allargs] = Mocky('%s(%s)' % (self.name, allargs))
        return self._calls[allargs]

    def __repr__(self):
        return self.name

    def __getattr__(self, name):
        if name not in self.__dict__:
            self.__dict__[name] = Mocky('%s.%s' % (self.name, name))
        return self.__dict__[name]

    def __setattr__(self, name, value):
        print "Set %s.%s to %r" % (self.name, name, value)
        self.__dict__[name] = value

I'm thinking about extending this so it also does dictionary-like access and a quiet mode for when it's obvious that you're setting this and calling that attribute.

I should be getting source code colouring for my blog like Marius. I have to find out how he does it.

posted at: 15:38 | 3 comments | category: /devel rss | permanent link | add to del.icio.us or digg it


< August 2006 >
SuMoTuWeThFrSa
   1 2 3 4 5
6 7 8 9101112
13141516171819
20212223242526
2728293031  

Feed of all categories: rss rss | atom

Categories: