Mobile devices promise to keep users connected. Yet, limited energy, data-transfer allowances, and cellular coverage reveal this assurance to be more a hope than a guarantee. This situation can be improved by increasing battery capacity, providing more generous data-transfer allowances, and expanding cellular coverage. We propose an alternative: modifying software to more efficiently use the available resources. In particular, many applications exhibit flexibility in when they must transfer data. For example, podcast managers can prefetch podcasts and photo sharing services can delay uploads until good conditions arise. More generally, applications that operate on data streams often have significant flexibility in when they update the stream.
More efficiently managing the available energy, the user’s data-transfer allowance and data availability can improve the user experience. Increasing battery life raises the user’s confidence that a charge will last the whole day, even with intense use. Alternatively, a smaller battery can be used decreasing the device’s monetary cost as well as its weight and size. Explicitly managing the data-transfer allowance enables users to choose less expensive data plans without fearing that the allowance will be exceeded, which may result in expensive overage fees and bill shock, a common occurrence in the US. Finally, accounting for availability by, e.g., prefetching data, hides spotty and weak network coverage and user-perceived latency is reduced.
We have encountered two main challenges to exploiting scheduling flexibility in data streams: predicting needed data and coordinating resource consumption. First, applications need to predict when and what to prefetch. Consider Alice, who listens to the latest episode of the hourly news on her 5 PM commute home. A simple policy prefetches episodes as they are published. As Alice only listens to the 4 PM or 5 PM episode, downloading episodes as they are published wastes energy and her data-transfer allowance. An alternative policy prefetches when power and WiFi are available, typically overnight. But, Alice wants the latest episode on her commute home, not the one from 6 AM. The scheduling algorithm needs to learn when and how Alice (the individual, not an aggregate model) uses data streams. The second challenge is coordinating the use of available resources. In particular, the data-transfer allowance and local storage must be partitioned between the applications and the user. This management should not interfere with the user by, e.g., exhausting the transfer allowance so that the user cannot surf the web, or causing an out-of-space error to occur when the user saves files. Further, the allocations should adapt to the user’s changing preferences.
Research on scheduling transmissions on smart phones has focused on reducing energy consumption by predicting near-term conditions. Bartendr delays transmissions until the signal strength is likely strong; TailEndr groups transmissions to amortizes energy costs; BreadCrumbs, among others, predicts WiFi availability to reduce energy spent needlessly scanning. contextual deadline, as predicted from observed user behavior. We also consider the cellular data-transmission allowance, which is an increasingly common constraint. Further, because we enable aggressive prefetching, we consider how to manage storage. Given multimedia data access patterns in which subscribed to data is used at most once, common replacement techniques, such as LRU, perform poorly.
In short, Woodchuck enables better scheduling of background data-stream updates to save energy, to make better use of data-transfer allowances, to improve disconnected operation, and to hide data-access latencies, all of which advance our ultimate goal of improving the user experience. To use Woodchuck, applications provide simple descriptions of transmission tasks. Woodchuck uses these and predictions of when, where and how data will be used based on application-input and historical data as well as when streams will be updated to schedule the requests so as to minimize battery use, to respect any data-transmission allowance, and to maximize the likelihood that data that the user accesses is available. We also consider how to manage storage for holding prefetched data.
Before detailing Woodchuck’s API, we provide a brief introduction to Woodchuck’s main concepts and some case studies of how we envision some applications could exploit Woodchuck.
Woodchuck’s model is relatively simple: there are managers, streams and objects. A manager represents an application (e.g., a podcast client). It contains streams. A stream represents a data source (e.g., a podcast feed). It references objects. An object represents some chunk of data (e.g., a podcast). Typically, users explicitly subscribe to a stream. The stream are regularly updated to discover new objects, which may be downloaded when convenient.
Managing the various object types is straightforward. An application registers a manager by calling ManagerRegister. Given a manager, the application registers streams using manager.StreamRegister. Similarly, an application registers objects with a stream using stream.ObjectRegister.
Associated with each object (Manager, Stream or Object) is a UUID and a cookie. The UUID uniquely identifies the object. The cookie is a free-form string that is uninterpreted by Woodchuck. It can be used by an application to store a database key or URL. This appears to greatly simplify the changes to the application as it eliminates the need for the application to manage a map between Woodchuck’s UUIDs and local stream and object identifiers.
Woodchuck makes an upcall, StreamUpdate and ObjectTransfer, to the application when the application should update a stream or transfer an object, respecitvely. After updating a stream, the application invokes stream.UpdateStatus and registers any newly discovered objects using stream.ObjectRegister. ObjectTransfer tells the application to transfer an object. After attempting the transfer, the application responds by calling object.TransferStatus.
When a user uses an object, an application can report this to Woodchuck using object.Used. The application can include a bitmask representing the portions of the object that were used. This assumes that there is some serial representation as is the case with videos and books.
When space becomes scarce, Woodchuck can delete files. When an application registers an object, it can include a deletion policy, which indicates whether the object is precious and may only be deleted by the user, whether Woodchuck may delete it without consulting the application, or whether to ask the application to delete the object. In the last case, Woodchuck uses the ObjectDeleteFiles upcall. The application responds using Object.FilesDeleted indicating either: the object has been deleted; the object should be preserved for at least X seconds longer; or, the object has been shrunk. Shrinking an object is useful for data like email where an email’s bulky attachments can be purged while still retaining the body.
One thing that I have not yet considered is an interface to allow applications to implement custom deletion policies. Although an application can delete a file at any file and communicate this to Woodchuck using the object.FilesDeleted interface, there is currently no mechanism for an application to say: “Tell me when there is storage pressure and I’ll find the best files to delete.”
To evaluate the applicability of the model, I’ve been using a few case studies: podcasts, blogs, weather and package repository updates. (Email and calendaring are similar to podcasts. Social networking (facebook, twitter, flickr) appears to be hybrid of podcasts and blogs.)
A podcast manager fits the proposed model very well. The podcast application registers one stream for each podcast subscription. When it updates a stream, it registers each new podcast episode as a Woodchuck object. When a podcast is viewed or listened to, it is easy to determine which parts were used.
A blog reader is similar to the podcast application: a subscription cleanly maps to Woodchuck’s stream concept and articles to Woodchuck’s object. Unlike the podcast application, new objects are typically transferred inline as part of a stream update. That is, a stream update consists not of an enumeration of new objects and references, but the objects’ contents. When such an application updates a stream, it registers new objects as usual and also marks them as having been transferred.
It should be relatively easy for Woodchuck to detect that the objects were delivered inline: the transfer time is the same as the stream update time. Nevertheless, I’ve exposed a stream property named stream.ObjectsMostInline, which an application can set if it expects this behavior.
Determining use for the application is also relatively straightforward: when an article is viewed, it has been used. It is possible to infer partial use for longer articles where scrolling is required. If the blog reader displays blogs using a continuous reader (like Google Reader), then this won’t work, but it is still possible for the application to infer use based on how fast the user scrolls.
At first glance, managing a package repository looks like managing podcasts. Unlike the podcast manager, prefetching most applications is useless: few users install more than dozens of applications. The few packages it makes sense to prefetch are updates to installed packages. Woodchuck can’t distinguish these on its own. It is possible to teach Woodchuck this by way of object’s priority property (org.woochuck.object.Priority). The application manager would then set this to high (e.g., 10) for packages that are installed and low (e.g., 1) for packages that are not installed. Woodchuck learns to trust the application based on actual use.
An alternative, planned approach is to provide a mechanism that allows applications to implement their own scheduling strategy. This can be down by having Woodchuck make an upcall indicating that the application should fetch the X MBs of most useful data.
If it turns out there are too many packages, just register those for which prefetching makes sense. But, always report the number of actually transferred packages when calling org.woochuck.stream.UpdateStatus.
The weather application is quite different from the podcast and blog applications. Most people, I think, are interested in monitoring a few locations at most, e.g., Baltimore and San Jose. In this case, the stream is not a series of immutable objects, but a series of object updates for a single object.
The best approach is to represent weather updates as a stream. Updating the stream means getting the latest weather. But then, the stream appears to have no objects. How do we track use? What about publication time? One solution is that after each update, the application creates a new object and marks it as having been transferred. The application should not register missed updates. Mostly likely it doesn’t even know how frequently the weather is updated. To indicate that a new update is available, create a new object. If the update is only available in the future, set the object’s TriggerEarliest property appropriately (org.woochuck.object.TriggerEarliest).
Woodchuck exposes its functionality via DBus. Applications, however, do not need to use this low-level interface. Instead, there is a C library that wraps Woodchuck’s functionality and Python modules. Application developers can ignore this section and read just about the interface they are interested in and only refer to this chapter for additional details, as required.
The C library provides a more convenient interface to access Woodchuck’s functionality than the low-level DBus interface. To do so, it makes a few assumption about how the streams and objects are managed. In particular, it assumes that a single application uses the specified manager and that it does so in a particular way. First, it assumes that the application only uses a top-level manager; hierarchical managers are not supported. It also assumes that streams and objects are uniquely identified by their respective cookies (thereby allowing the use of org.woodchuck.LookupManagerByCookie()). For most applications, these limitations should not present a burden.
The C library currently only works with programs using the glib mainloop and the gobject object system.
The C library is currently only documented in the header files <woodchuck/woodchuck.h> and <woodchuck/gwoodchuck.h>. Please refer to it for reference. Note, however, that the interface is very similar to the PyWoodchuck interface.
There are two python modules for interacting with a Woodchuck server: pywoodchuck and woodchuck. pywoodchuck is a high-level module, which provides a Pythonic interface. It hides a fair amount of complexity while sacrificing only a small amount of functionality. It is recommended for most applications. The woodchuck module is a thin wrapper on top of the DBus interface.
The pywoodchuck module provides a high-level Pythonic interface to Python.
A high-level, pythonic interface to Woodchuck.
This module assumes that a single application uses the specified manager and that it does so in a particular way. First, it assumes that the application only uses a top-level manager; hierarchical managers are not supported. It also assumes that streams and objects are uniquely identified by their respective cookies (thereby allowing the use of org.woodchuck.LookupManagerByCookie()). For most applications, these limitations should not present a burden.
If applications violate these assumptions, i.e., by manipulating the manager in an incompatible way using a low-level interface, PyWoodchuck may refuse to work with the manager.
Note
In order to process upcalls, your application must use a main loop. Moreover, DBus must know about the main loop. If you are using glib, before accessing the session bus, run:
from dbus.mainloop.glib import DBusGMainLoop
DBusGMainLoop(set_as_default=True)
or, if you are using Qt, run:
from dbus.mainloop.qt import DBusQtMainLoop
DBusQtMainLoop(set_as_default=True)
A PyWoodchuck instance behaves like a dictionary: iterating over it yields the streams contained therein; streams can be indexed by their stream identifier; and, stream can also be removed (_Stream.unregister()) using del. Note: you cannot register a stream by assigning a value to a key.
Registers the application with Woodchuck, if not already registered.
Parameters: |
|
---|
Example: if upcalls are not required:
import pywoodchuck
w = pywoodchuck.PyWoodchuck("RSS Reader", "org.rssreader")
Example: if you are interested in the stream_update_cb() and object_transfer_cb() upcalls:
import pywoodchuck
class mywoodchuck (pywoodchuck.PyWoodchuck):
def stream_update_cb(self, stream):
print "stream update called on %s" % (stream.identifier,)
def object_transfer_cb(self, stream, object,
version, filename, quality):
print "object transfer called on %s in stream %s" \
% (object.identifier, stream.identifier);
w = mywoodchuck("RSS Reader", "org.rssreader")
The returned object behaves like a dict, which maps stream identifiers to _Stream objects.
Returns: | Whether the Woodchuck daemon is available. |
---|
If the Woodchuck daemon is not available, all other methods will raise a woodchuck.WoodchuckUnavailableError.
Example:
import pywoodchuck
w = pywoodchuck.PyWoodchuck("RSS Reader", "org.rssreader")
if not w.available ():
print "Woodchuck functionality not available."
else:
print "Woodchuck functionality available."
Register a new stream with Woodchuck.
Parameters: |
|
---|---|
Returns: | Returns a _Stream instance. |
Example:
import pywoodchuck
import woodchuck
w = pywoodchuck.PyWoodchuck("RSS Reader", "org.rssreader")
w.stream_register("http://feeds.boingboing.net/boingboing/iBag",
"BoingBoing")
try:
w.stream_register("http://feeds.boingboing.net/boingboing/iBag",
"BoingBoing")
except woodchuck.ObjectExistsError as exception:
print "Stream already registered:", exception
del w["http://feeds.boingboing.net/boingboing/iBag"]
List all streams managed by this application.
Returns: | Returns a list of _Stream instances. |
---|
Example:
import pywoodchuck
w = pywoodchuck.PyWoodchuck("Application", "org.application")
w.stream_register("id:foo", "Foo")
w.stream_register("id:bar", "Bar")
for s in w.streams_list ():
print "%s: %s" % (s.human_readable_name, s.identifier)
del w[s.identifier]
Note
This is equivalent to iterating over the PyWoodchuck instance:
import pywoodchuck
w = pywoodchuck.PyWoodchuck("Application", "org.application")
w.stream_register("id:foo", "Foo")
w.stream_register("id:bar", "Bar")
for s in w.values ():
print "%s: %s" % (s.human_readable_name, s.identifier)
del w[s.identifier]
Unregister the indicated stream and any objects in contains.
Note
This function is an alias for _Stream.unregister():
pywoodchuck[stream_identifier].unregister()
It is also equivalent to using the del operator except instead of raising woodchuck.NoSuchObject, del raises KeyError if the object does not exist:
del pywoodchuck[stream_identifier].
Tell Woodchuck that a stream has been successfully updated.
Parameters: | stream_identifier – The stream’s identifier. |
---|
The remaining parameters are passed through to _Stream.updated().
Tell Woodchuck that a stream update failed.
Parameters: | stream_identifier – The stream’s identifier. |
---|
Note
This function is an alias for _Stream.update_failed():
pywoodchuck[stream_identifier].update_failed(...)
The remaining parameters are passed through to _Stream.update_failed().
Register an object.
Note
This function is an alias for _Stream.object_register():
pywoodchuck[stream_identifier].object_register (...)
Parameters: | stream_identifier – The stream’s identifier. |
---|
The remaining parameters are passed through to _Stream.object_register().
List the objects in a stream.
Note
This function is an alias for _Stream.objects_list():
pywoodchuck[stream_identifier].objects_list (...)
And for iterating over a _Stream object:
for obj in pywoodchuck[stream_identifier].values (): pass
Parameters: | stream_identifier – The stream’s identifier. |
---|
Tell Woodchuck that an object was successfully transferred.
Note
This function is an alias for _Stream.object_transferred():
pywoodchuck[stream_identifier].object_transferred (...)
Parameters: |
|
---|
The remaining parameters are passed through to _Stream.object_transferred().
Indicate that the program failed to transfer the object.
Note
This function is an alias for _Stream.object_transfer_failed():
pywoodchuck[stream_identifier].object_transfer_failed (...)
Parameters: |
|
---|
The remaining parameters are passed through to _Stream.object_transfer_failed().
Indicate that the object has been used.
Note
This function is an alias for _Object.used():
pywoodchuck[stream_identifier][object_identifier].used (...)
Parameters: |
|
---|
The remaining parameters are passed through to _Object.used().
Indicate that the files associated with the object have been deleted, compressed (e.g., an email attachment, but not the body, was deleted) or that a deletion request has been vetoed, because, e.g., the application thinks the user still needs the data.
Note
This function is an alias for _Object.files_deleted():
pywoodchuck[stream_identifier][object_identifier].files_deleted (...)
Parameters: |
|
---|
The remaining parameters are passed through to _Object.files_deleted().
Unregister an object.
Note
This function is an alias for _Object.unregister():
pywoodchuck[stream_identifier][object_identifier].unregister ()
Parameters: |
|
---|
Get a property associated with the manager (i.e., the application).
Parameters: | property – A property, e.g., enabled. |
---|
See stream_property_set() for an example use of a similar function.
Set a property associated with the manager (i.e., the application).
Parameters: |
|
---|
See stream_property_set() for an example use of a similar function.
Get a stream’s property.
Parameters: |
|
---|
See stream_property_set() for an example use of this function.
Set a stream’s property.
Parameters: |
|
---|
Example:
import pywoodchuck
import woodchuck
w = pywoodchuck.PyWoodchuck("HMail", "org.hmail")
w.stream_register("user@provider.com/INBOX", "Provider Inbox",
freshness=30*60)
print w.stream_property_get ("user@provider.com/INBOX", "freshness")
w.stream_property_set ("user@provider.com/INBOX",
"freshness", 15*60)
print w.stream_property_get ("user@provider.com/INBOX", "freshness")
Note
Properties can also be get and set by accessing the equivalently named attributes. Thus, the above code could be rewritten as follows:
import pywoodchuck
import woodchuck
w = pywoodchuck.PyWoodchuck("HMail", "org.hmail")
w.stream_register("user@provider.com/INBOX", "Provider Inbox",
freshness=30*60)
print["user@provider.com/INBOX].freshness
w["user@provider.com/INBOX"].freshness = 15*60
print w["user@provider.com/INBOX"].freshness
Get an object’s property.
Parameters: |
|
---|
See stream_property_set() for an example use of a similar function.
Set an object’s property.
Parameters: |
|
---|
See stream_property_set() for an example use of a similar function.
Virtual method that should be implemented by the child class if it is interested in receiving object transferred notifications (org.woodchuck.upcall.ObjectTransferred()).
This upcall is invoked when Woodchuck transfers an object on behalf of a manager. This is only done for objects using the simple transferer.
Parameters: |
|
---|
Example: for an example of how to implement an upcall, see the opening example to PyWoodchuck.
Virtual method that should be implemented by the child class if it is interested in receiving stream update notifications (org.woodchuck.upcall.StreamUpdate()).
This upcall is invoked when a stream should be updated. The application should update the stream and call stream_updated() or stream_update_failed(), as appropriate.
Parameters: | stream – The stream, an instance of _Stream. |
---|
Example: for an example of how to implement an upcall, see the opening example to PyWoodchuck.
Virtual method that should be implemented by the child class if it is interested in receiving object transfer notifications (org.woodchuck.upcall.ObjectTransfer()).
This upcall is invoked when an object should be transferred. The application should transfer the object and call either object_transferred() or object_transfer_failed(), as appropriate.
Parameters: |
|
---|
Example: for an example of how to implement an upcall, see the opening example to PyWoodchuck.
Virtual method that should be implemented by the child class if it is interested in receiving deletion requests (org.woodchuck.upcall.ObjectDeleteFiles()).
This upcall is invoked when an object’s files should be transferred. The application should respond with object_files_deleted().
Parameters: |
---|
Example: for an example of how to implement an upcall, see the opening example to PyWoodchuck.
Encapsulates a Woodchuck stream. This object should never be explicitly instantiated by user code. Instead, use PyWoodchuck[stream_identifier] to obtain a reference to an instance.
A _Stream instance behaves like a dictionary: iterating over it yields the objects contained therein; objects can be indexed by their object identifier; and, objects can also be removed (_Object.unregister()) using del. Note: you cannot register an object by assigning a value to a key.
Stream properties, such as freshness, can be get and set by assigning to the like named instance attributes, e.g.:
stream.freshness = 60 * 60
Parameters: |
|
---|
Unregister the stream and any objects it contains. This just causes Woodchuck to become unaware of the stream and delete any metadata about it; this does not actually remove any objects’ files.
Note
This function is eqivalent to calling:
del pywoodchuck[stream_identifier]
Example: See PyWoodchuck.stream_register() for an example use of this function.
Tell Woodchuck that the stream has been successfully updated. Call this function whenever the stream is successfully updated, not only in response to a stream_update_cb() upcall. If a stream update fails, this should be reported using _Stream.update_failed().
Parameters: |
|
---|
Example of reporting a stream update for which five new objects were discovered and all of which were delivered inline:
import pywoodchuck
import time
w = pywoodchuck.PyWoodchuck("Application", "org.application")
w.stream_register("stream identifier", "human_readable_name")
transfer_time = int (time.time ())
# Perform the transfer
transfer_duration = int (time.time ()) - transfer_time
w["stream identifier"].updated (
transferred_up=2048, transferred_down=64000,
transfer_time=transfer_time,
transfer_duration=transfer_duration,
new_objects=5, objects_inline=5)
del w["stream identifier"]
Note
The five new objects should immediately be registered using object_register() and marked as transferred using _Object.transferred().
Tell Woodchuck that a stream update failed. Call this function whenever a stream update is attempted, not only in response to a stream_update_cb() upcall.
Parameters: |
|
---|
Example of reporting a failed stream update:
import pywoodchuck
import woodchuck
w = pywoodchuck.PyWoodchuck("Application", "org.application")
w.stream_register("stream identifier", "human_readable_name")
# Try to transfer the data.
w["stream identifier"].update_failed (
woodchuck.TransferStatus.TransientNetwork,
transferred_up=1038, transferred_down=0)
del w["stream identifier"]
Register an object.
Parameters: |
|
---|---|
Expected_size : | The expected amount of disk space required after this transfer completes. If this object represents an upload and space will be freed after the transfer completes, this should be negative. |
Versions : | An array of [URL, expected_size, expected_transfer_up, expected_transfer_down, utility, use_simple_transferer`] specifying alternate versions of the object. expected_size is the expected amount of disk space required when this transfer completes. expected_transfer_up is the expected upload size, in bytes. expected_transfer_down is the expected transfer size, in bytes. utility is the utility of this version relative to other versions. The utility is assumed to be a linear function, i.e.,a version with 10 has twice as much value as another version with 5. use_simple_transferer is a boolean indicating whether Woodchuck should use its simple transferer to fetch the object. |
Returns: | Returns a _Object instance. |
Note
The caller may provide either expected_size or versions, but not both.
List the objects in the stream.
Returns: | Returns a list of _Object instances. |
---|
Note
This function is equivalent to iterating over the stream:
for obj in stream.values ():
print obj.identifier, obj.human_readable_name
Example:
import pywoodchuck
w = pywoodchuck.PyWoodchuck("Application", "org.application")
w.stream_register("stream identifier", "human_readable_name")
w["stream identifier"].object_register(
"object 1", "human_readable_name 1")
w["stream identifier"].object_register(
"object 2", "human_readable_name 2")
w["stream identifier"].object_register(
"object 3", "human_readable_name 3")
for obj in w["stream identifier"].objects_list ():
print "%s: %s" % (obj.human_readable_name, obj.identifier)
del w["stream identifier"]["object 2"]
for obj in w["stream identifier"].objects_list ():
print "%s: %s" % (obj.human_readable_name, obj.identifier)
del w["stream identifier"]
Tell Woodchuck that an object was successfully transferred.
This function is a wrapper for _Object.transferred(). It takes one additional argument, the object’s identifier. Like _Object.transferred(), this function marks the object as transferred. Unlike _Object.transferred(), if the object is not yet registered, this function first registers it setting human_readable_name set to object_identifier.
Example:
import pywoodchuck
import woodchuck
w = pywoodchuck.PyWoodchuck("Podcasts", "org.podcasts")
w.stream_register("http://podcast.site/podcasts/SomePodcast.rss",
"Some Podcast")
w["http://podcast.site/podcasts/SomePodcast.rss"].object_transferred(
"http://podcast.site/podcasts/SomePodcast/Episode-15.ogg",
indicator=(woodchuck.Indicator.ApplicationVisual
|woodchuck.Indicator.DesktopSmallVisual
|woodchuck.Indicator.ObjectSpecific),
transferred_up=39308, transferred_down=991203,
files=[ ["/home/user/Podcasts/SomePodcast/Episode-15.ogg",
True,
woodchuck.DeletionPolicy.DeleteWithoutConsultation], ])
del w["http://podcast.site/podcasts/SomePodcast.rss"]
Indicate that the program failed to transfer the object.
This function is a wrapper for _Object.transfer_failed(). It takes one additional argument, the object’s identifier. Like _Object.transfer_failed(), this function marks the object as having failed to be transferred. Unlike _Object.transfer_failed(), if the object is not yet registered, this function first registers it setting human_readable_name set to object_identifier.
Indicate that the files associated with the object have been deleted, compressed (e.g., an email attachment, but not the body, was deleted) or that a deletion request has been vetoed, because, e.g., the application thinks the user still needs the data.
Note
This function is an alias for _Object.files_deleted():
pywoodchuck[stream_identifier][object_identifier].files_deleted (...)
Parameters: | object_identifier – The object’s identifier. |
---|
The remaining parameters are passed through to _Object.files_deleted().
Encapsulates a Woodchuck object. This object should never be explicitly instantiated by user code. Instead, use PyWoodchuck[stream_identifier][object_identifier] to obtain a reference to an instance.
Object properties, such as publication time, can be gotten and set by assigning to the like named instance attributes, e.g.:
object.publication_time = time.time ()
Parameters: |
|
---|
Unregister the object. This just causes Woodchuck to become unaware of the object and delete any associated metadata; this does not actually remove any of the object’s files.
Note
This function is an alias for:
del pywoodchuck[stream_identifier][object_identifier]
Tell Woodchuck that the object was successfully transferred.
Call this function whenever an object transfer is attempted, not only in response to a object_transfer_cb() upcall.
Parameters: |
|
---|
Example:
import pywoodchuck
import woodchuck
w = pywoodchuck.PyWoodchuck("Podcasts", "org.podcasts")
w.stream_register("http://podcast.site/SomePodcast.rss",
"Some Podcast")
w["http://podcast.site/SomePodcast.rss"].object_register(
"http://podcast.site/SomePodcast/Episode-15.ogg",
"Episode 15: Title")
# Transfer the file.
w["http://podcast.site/SomePodcast.rss"]\
["http://podcast.site/SomePodcast/Episode-15.ogg"].transferred(
indicator=(woodchuck.Indicator.ApplicationVisual
|woodchuck.Indicator.DesktopSmallVisual
|woodchuck.Indicator.ObjectSpecific),
transferred_up=39308, transferred_down=991203,
files=[ ["/home/user/SomePodcast/Episode-15.ogg",
True,
woodchuck.DeletionPolicy.DeleteWithoutConsultation], ])
del w["http://podcast.site/SomePodcast.rss"]
Indicate that the program failed to transfer the object.
Parameters: |
|
---|
Example: For an example of a similar function, see _Stream.stream_update_failed().
Indicate that the object has been used.
Parameters: |
|
---|
Example: indicate that the user view the first 2 minutes of a 64 minute video Podcast:
import pywoodchuck
import time
w = pywoodchuck.PyWoodchuck("Podcasts", "org.podcasts")
w.stream_register("http://videocast.site/podcasts/Videocast.rss",
"Video Podcast")
w["http://videocast.site/podcasts/Videocast.rss"].object_register(
"http://videocast.site/podcasts/Episode-15.ogv",
"Episode 15: Title")
# User clicks play:
start = int (time.time ())
use_mask = 0
length = 64
# Periodically sample the stream's position and update use_mask.
for pos in (1, 2):
use_mask |= 1 << int (64 * (pos / float (length)) - 1)
# User clicks stop after 2 minutes. `use_mask` is now
# 0x3: the least two significant bits are set.
end = int (time.time ())
w["http://videocast.site/podcasts/Videocast.rss"]\
["http://videocast.site/podcasts/Episode-15.ogv"].used (
start, end - start, use_mask)
del w["http://videocast.site/podcasts/Videocast.rss"]
Indicate that the files associated with the object have been deleted, compressed (e.g., an email attachment, but not the body, was deleted) or that a deletion request has been vetoed, because, e.g., the application thinks the user still needs the data.
Parameters: |
|
---|
Example: Indicating that an email attachment has been deleted, but not the email’s body:
import pywoodchuck
import woodchuck
w = pywoodchuck.PyWoodchuck("HMail", "org.hmail")
w.stream_register("user@provider.com/INBOX", "Provider Inbox")
w["user@provider.com/INBOX"].object_register(
"2721812449",
"Subject Line")
w["user@provider.com/INBOX"]["2721812449"].transferred (
transferred_up=3308, transferred_down=991203,
files=[ ["/home/user/Maildir/.inbox/cur/2721812449",
True,
woodchuck.DeletionPolicy.DeleteWithConsultation], ])
w["user@provider.com/INBOX"]["2721812449"].files_deleted (
woodchuck.DeletionResponse.Compressed, 1877)
del w["user@provider.com/INBOX"]
The woodchuck module is a low-level wrapper of the DBus interface. Each of Woodchuck’s object types is mirrored by a similarly named Python class.
The woodchuck module uses a factory for managing instantiations of the objects. In particular, the factory ensures that there is at most one Python object per Woodchuck object. That is, the same Python object is shared by all users of a given Woodchuck object.
The Woodchuck object wraps the top-level Woodchuck interface.
Return a reference to the top-level Woodchuck singleton.
Note: There is at most a single _Woodchuck instance. In other words, the Python object is shared among all users.
The top-level Woodchuck class.
Register a new top-level manager.
Parameters: |
|
---|---|
Returns: | A _Manager object. |
Example:
import woodchuck
w = woodchuck.Woodchuck ()
manager = w.manager_register(
only_if_cookie_unique=True,
human_readable_name="RSS Reader",
cookie="org.rssreader",
dbus_service_name="org.rssreader")
manager.unregister ()
List known managers.
Parameters: | recursive – If True, list all managers. Otherwise, only list top-level managers. |
---|---|
Returns: | An array of _Manager |
Example:
import woodchuck
print "The top-level managers are:"
for m in woodchuck.Woodchuck().list_managers (False):
print m.human_readable_name + ": " + m.cookie
Return the set of managers with the specified cookie.
Parameters: |
|
---|---|
Returns: | An array of _Manager |
Example:
import woodchuck
import random
w = woodchuck.Woodchuck()
cookie=str (random.random())
m = w.manager_register(True, cookie=cookie,
human_readable_name="Test")
managers = w.lookup_manager_by_cookie(cookie, False)
assert len (managers) == 1
assert managers[0].UUID == m.UUID
assert managers[0].cookie == cookie
m.unregister (True)
The _Manager class wraps a Woodchuck manager.
Return a reference to a _Manager object. This function does not actually register a manager; a manager is assumed to already exist. This function should not normally be called from user code. Instead, call _Woodchuck.manager_register() or _Woodchuck.lookup_manager_by_cookie() to get a _Manager object.
Parameters: |
|
---|---|
Returns: | A _Manager object with the specified properties. |
Note: There is at most a single _Manager instance per Woodchuck manager object. In other words, the Python object is shared among all users.
Instantiate a Woodchuck._Manager. Instantiating this object does not actually register a manager; the manager is assumed to already exist. A Woodchuck._Manager object should should not normally be directly instantiated from user code. Instead, use a method that returns an _Manager, such as _Woodchuck.manager_register() or _Woodchuck.lookup_manager_by_cookie() to get a _Manager object.
Parameters: |
|
---|
Unregister the manager object thereby causing Woodchuck to permanently forget about the manager and any streams and objects it contained.
Parameters: | only_if_empty – If True, this method invocation only suceeds if the manager has no children, i.e., no descendent managers and no streams. |
---|
Example:
try:
manager.unregister (True)
except woodchuck.NoSuchObject as exception:
print "Can't remove stream %s: Does not exist: %s" \
% (str (manager), exception)
except woodchuck.ObjectExistsError as exception:
print "Can't remove manager %s: Not empty: %s" \
% (str (manager), exception)
Register a child manager.
Parameters: |
|
---|---|
Returns: | A _Manager object. |
Example:
import woodchuck
w = woodchuck.Woodchuck ()
manager = w.manager_register(
only_if_cookie_unique=True,
human_readable_name="Web Browser",
cookie="org.webbrowser",
dbus_service_name="org.webbrowser")
web_cache = manager.manager_register(
only_if_cookie_unique=False,
human_readable_name="Web Cache")
download_later = manager.manager_register(
only_if_cookie_unique=False,
human_readable_name="Downloads for Later")
manager.unregister (only_if_empty=False)
List managers that are a descendent of this one.
Parameters: | recursive – If True, list all descendent managers. Otherwise, only list managers that are an immediate descendent. |
---|---|
Returns: | An array of _Manager |
See _Woodchuck.list_managers() for an example using a similar function.
Return the set of managers with the specified cookie that are a descendent of this one.
Parameters: |
|
---|---|
Returns: | An array of _Manager |
See _Woodchuck.lookup_manager_by_cookie() for an example.
Register a new stream.
Parameters: |
|
---|---|
Returns: | A _Stream object. |
Example:
import woodchuck
import random
w = woodchuck.Woodchuck()
cookie=str (random.random())
m = w.manager_register(True, cookie=cookie,
human_readable_name="Test Manager")
s = m.stream_register(True, cookie=cookie,
human_readable_name="Test Stream")
print m.list_streams ()
m.unregister (only_if_empty=False)
List this manager’s streams.
Returns: | An array of _Stream |
---|
See _Woodchuck.list_managers() for an example using a similar function.
Return the set of streams with the specified cookie.
Parameters: | cookie – The cookie to match. |
---|---|
Returns: | An array of _Stream |
See _Woodchuck.lookup_manager_by_cookie() for an example using a similar function.
Request that Woodchuck begin making upcalls for this manager.
Parameters: | descendents_too – If True, also makes upcalls for any descendent managers. |
---|---|
Returns: | An opaque handle, which must be passed to _Manager.feedback_unsubscribe(). |
At most, a single subscription is obtained per Manager. Thus, multiple subscriptions share the same handle. To stop receiving feedback, _Manager.feedback_unsubscribe() must be called the same number of times.
Example:
subscription = manager.feedback_subscribe (True)
...
manager.feedback_unsubscribe(subscription)
To actually receive upcalls refer to woodchuck.Upcalls.
Cancel an upcall subscription.
Parameters: | handle – The value returned by a previous call to _Manager.feedback_subscribe(). |
---|
Invoke org.woodchuck.manager.FeedbackAck.
Instantiate a Woodchuck._Stream. Instantiating this object does not actually register a stream; the stream is assumed to already exist. A Woodchuck._Stream object should should not normally be directly instantiated from user code. Instead, use a method that returns an _Stream, such as _Manager.stream_register() or _Manager.lookup_stream_by_cookie() to get a _Stream object.
Parameters: |
|
---|
Unregister the stream object thereby causing Woodchuck to permanently forget about the stream and any object it contained.
Parameters: | only_if_empty – If True, this method invocation only suceeds if the stream contains no objects. |
---|
Example:
try:
stream.unregister (True)
except woodchuck.NoSuchObject as exception:
print "Can't remove stream %s: Does not exist: %s"
% (str (stream), exception)
except woodchuck.ObjectExistsError as exception:
print "Can't remove stream %s: Not empty: %s"
% (str (stream), exception)
Register a new object.
Parameters: |
|
---|---|
Returns: | A _Object object. |
See _Manager.stream_register() for an example using a similar function.
List this stream’s objects.
Returns: | An array of _Object |
---|
See _Woodchuck.list_managers() for an example using a similar function.
Return the set of objects with the specified cookie.
Parameters: | cookie – The cookie to match. |
---|---|
Returns: | An array of _Object |
See _Woodchuck.lookup_manager_by_cookie() for an example using a similar function.
Tell Woodchuck that the stream has been updated. Call this function whenever a stream is updated, not only in response to a _Upcalls.stream_update_cb() upcall.
Parameters: |
|
---|
Example of reporting a stream update for which five new objects were discovered and all of which were delivered inline:
import woodchuck
import time
...
transfer_time = int (time.time ())
...
# Perform the transfer
...
transfer_duration = int (time.time ()) - transfer_time
stream.update_status (status=0,
transferred_up=2048,
transferred_down=64000,
transfer_time=transfer_time,
transfer_duration=transfer_duration,
new_objects=5,
objects_inline=5)
Note: The five new objects should immediately be registered using _Stream.object_register() and marked as transferred using _Object.transfer_status().
Example of a failed update due to a network problem, e.g., the host is unreachable:
stream.update_status (woodchuck.TransientNetwork,
transferred_up=100)
The local representation for a Woodchuck object.
Instantiate a Woodchuck._Object. Instantiating this object does not actually register an object; the object is assumed to already exist. A Woodchuck._Object object should should not normally be directly instantiated from user code. Instead, use a method that returns an _Object, such as _Stream.object_register() or _Stream.lookup_object_by_cookie() to get a _Object object.
Parameters: |
|
---|
Unregister the object object thereby causing Woodchuck to permanently forget about the object.
See _Stream.unregister() for an example using a similar function.
Request that Woodchuck transfer the object. This only makes sense for object’s that use Woodchuck’s simple transferer.
Parameters: | request_type – Whether the request is user initiated or application initiated. See TransferStatus for possible values. |
---|
Tell Woodchuck that the object has been transferred. Call this function whenever an object is transferred (or uploaded), not only in response to a _Upcalls.object_transfer_cb() upcall.
Parameters: |
|
---|
Example of reporting an object transfer for an object that Woodchuck can deleted without consulting the user:
transfer_time = int (time.time ())
...
# Perform the transfer
...
transfer_duration = int (time.time ()) - transfer_time
stream.update_status(
status=0,
transferred_up=4096,
transferred_down=1024000,
transfer_time=transfer_time,
transfer_duration=transfer_duration,
files=( ("/home/user/Podcasts/Foo/Episode1.ogg", True,
woodchuck.DeletionPolicy.DeleteWithoutConsultation),))
Mark the object as having been used.
Parameters: |
|
---|
Example: Indicate that that the first two minutes of an hour-long video were viewed:
object.used(start_time, 120, 0x3)
Indicate that some or all of the object’s files have been deleted. This should be called whenever an object’s files are deleted, not only in response to Upcalls.object_delete_files_cb().
Parameters: |
|
---|
Example: An email’s attachments are purged, but the body is preserved:
object.files_deleted (woodchuck.DeletionResponse.Compressed,
2338)
A thin wrapper around org.woodchuck.upcalls.
To use this class, implement your own class, which inherits from this one and overrides the virtual methods of the upcalls that you are interested in (Upcalls.object_transferred_cb(), Upcalls.stream_update_cb(), Upcalls.object_transfer_cb() and object_delete_files_cb()). Instantiate the class and then call woodchuck.feedback_subscribe() to begin receiving feedback.
Example:
class Upcalls(woodchuck.Upcalls):
def object_transferred_cb (self, **kwargs):
# Transfer the kwargs[object_UUID] object.
...
upcalls = Upcalls ()
subscription = Manager.feedback_subscribe (False)
Note
In order to process upcalls, your application must use a main loop. Moreover, DBus must know about the main loop. If you are using glib, before accessing the session bus, run:
from dbus.mainloop.glib import DBusGMainLoop DBusGMainLoop(set_as_default=True)
or, if you are using Qt, run:
from dbus.mainloop.qt import DBusQtMainLoop DBusQtMainLoop(set_as_default=True)
Parameters: | path – The object that will receive the upcalls from woodchuck. |
---|
Virtual method that should be implemented by the child class if it is interested in org.woodchuck.upcall.ObjectTransferred upcalls.
This upcall is invoked when Woodchuck transfers an object on behalf of a manager. This is only done for objects using the simple transferer.
Parameters: |
|
---|
Virtual method that should be implemented by the child class if it is interested in org.woodchuck.upcall.StreamUpdate upcalls.
This upcall is invoked when a stream should be updated. The application should update the stream and call _Stream.update_status().
Parameters: |
|
---|
Virtual method that should be implemented by the child class if it is interested in org.woodchuck.upcall.ObjectTransfer upcalls.
This upcall is invoked when Woodchuck transfers an object on behalf of a manager. This is only done for objects using the simple transferer.
Parameters: |
|
---|
Virtual method that should be implemented by the child class if it is interested in org.woodchuck.upcall.ObjectDeleteFiles upcalls.
This upcall is invoked when Woodchuck wants a manager to free disk space.
Parameters: |
|
---|
A low-level wrapper of the org.woodchuck DBus interfaces.
Values for the request_type argument of _Object.transfer().
The user initiated the transfer request.
The application initiated the transfer request.
Values for the Indicator argument of woodchuck._Object.transfer_status(), woodchuck._Stream.update_status() and woodchuck.Upcalls.object_transferred_cb().
The transfer was successful.
An unspecified transient error occurred.
A transient network error occured, e.g., the host was unreachable.
A transient error occured during the transfer.
An unspecified hard error occured. Don’t try again.
A hard error, the object is gone, occured.
Values for the Indicator argument of woodchuck._Object.transfer_status(), woodchuck._Stream.update_status().
An audio sound was emitted.
An visual notification was displayed in the application.
A small visual notification was displayed on the desktop, e.g., in the system tray.
A large visual notification was displayed on the desktop.
An external visual notification was displayed, e.g., an LED was blinked.
The device vibrated.
The notification was object specific.
The notification was stream-wide, i.e., an aggregate notification for all updates in the stream.
The notification was manager-wide, i.e., an aggregate : notification for multiple stream updates.
It is unknown whether an indicator was shown.
Values for the deletion_policy argument of woodchuck._Object.transfer_status().
The file is precious and will only be deleted by the user.
Woodchuck may delete the file without consulting the application.
Woodchuck may ask the application to delete the file.
Values for the Update arguments of woodchuck._Object.files_deleted()
The files associated with the object were deleted.
The application refuses to delete the object.
The application compressed the object, e.g., for an email, it
Base class for exceptions in this model. args[0] contains a more detailed description of the error.
While invoking a Woodchuck method, a DBus error org.woodchuck.GenericError occured.
While invoking a Woodchuck method, a DBus error org.freedesktop.DBus.Error.UnknownObject occured.
While invoking a Woodchuck method, a DBus error org.woodchuck.ObjectExists occured.
While invoking a Woodchuck method, a DBus error org.woodchuck.MethodNotImplemented occured.
While invoking a Woodchuck method, a DBus error org.woodchuck.InternalError occured.
While invoking a Woodchuck method, a DBus error org.woodchuck.InvalidArgs occured.
While invoking a Woodchuck method, an unknown DBus error with prefix org.woodchuck occured.
The woodchuck server is unavailable. For whatever reason, it couldn’t be started. This is a Python specific exception.