diff options
author | Olivier Tilloy <olivier@tilloy.net> | 2010-02-16 17:10:07 +0100 |
---|---|---|
committer | Olivier Tilloy <olivier@tilloy.net> | 2010-02-16 17:10:07 +0100 |
commit | 6f165359c2e67e35b328fdb5b6ad6aa8f22cdab9 (patch) | |
tree | 42ef9134b7a9386680eab1d5bb6e3e76d539470e | |
parent | b2b0165fa924426fa8bb17a61c7f655fd5ccac7e (diff) | |
parent | 871d24f9d959fe5cf9664bbb4f00ea94ff9b58f0 (diff) | |
download | pyexiv2-6f165359c2e67e35b328fdb5b6ad6aa8f22cdab9.tar.gz |
Complete documentation generated with sphinx.
-rw-r--r-- | .bzrignore | 1 | ||||
-rw-r--r-- | SConstruct | 19 | ||||
-rw-r--r-- | doc/SConscript | 29 | ||||
-rw-r--r-- | doc/api.rst | 58 | ||||
-rw-r--r-- | doc/conf.py | 196 | ||||
-rw-r--r-- | doc/developers.rst | 124 | ||||
-rw-r--r-- | doc/index.rst | 43 | ||||
-rw-r--r-- | doc/tutorial.rst | 234 | ||||
-rw-r--r-- | src/SConscript | 3 | ||||
-rw-r--r-- | src/pyexiv2/__init__.py | 6 | ||||
-rw-r--r-- | src/pyexiv2/exif.py | 73 | ||||
-rw-r--r-- | src/pyexiv2/iptc.py | 70 | ||||
-rw-r--r-- | src/pyexiv2/metadata.py | 77 | ||||
-rw-r--r-- | src/pyexiv2/utils.py | 181 | ||||
-rw-r--r-- | src/pyexiv2/xmp.py | 91 |
15 files changed, 952 insertions, 253 deletions
@@ -1,2 +1,3 @@ .sconsign.dblite build +doc/_build/* @@ -1,4 +1,19 @@ -#!/usr/bin/python # -*- coding: utf-8 -*- -SConscript('src/SConscript', variant_dir='build', duplicate=0) +def build_lib(): + SConscript('src/SConscript', variant_dir='build', duplicate=0) + +def build_doc(): + SConscript('doc/SConscript') + +if not BUILD_TARGETS: + # Default target: lib + build_lib() +else: + if 'lib' in BUILD_TARGETS or 'install' in BUILD_TARGETS: + build_lib() + if 'doc' in BUILD_TARGETS: + # Note: building the doc requires the lib to be built and the pyexiv2 + # module to be in the python path. + build_doc() + diff --git a/doc/SConscript b/doc/SConscript new file mode 100644 index 0000000..333e65c --- /dev/null +++ b/doc/SConscript @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- + +import inspect +import os.path +import sys +from sphinx.application import Sphinx + +# Build HTML documentation using sphinx +script = inspect.currentframe().f_code.co_filename +srcdir = os.path.dirname(script) +confdir = srcdir +outdir = os.path.join(srcdir, '_build') +doctreedir = os.path.join(outdir, '.doctrees') + +sphinx = Sphinx(srcdir, confdir, outdir, doctreedir, 'html', {}, sys.stdout) + +sources = [os.path.join(srcdir, doc + '.rst') + for doc in sphinx.builder.get_outdated_docs()] +output = [os.path.join(outdir, doc + '.html') + for doc in sphinx.builder.get_outdated_docs()] + +def build_doc(target, source, env): + sphinx.build(all_files=False, filenames=[]) + return sphinx.statuscode + +env = Environment() +doc = env.Command(output, sources, build_doc) +env.Alias('doc', doc) + diff --git a/doc/api.rst b/doc/api.rst new file mode 100644 index 0000000..3a26568 --- /dev/null +++ b/doc/api.rst @@ -0,0 +1,58 @@ +API documentation +================= + +pyexiv2 +####### + +.. module:: pyexiv2 +.. autodata:: version_info +.. autodata:: __version__ +.. autodata:: exiv2_version_info +.. autodata:: __exiv2_version__ + +pyexiv2.metadata +################ + +.. module:: pyexiv2.metadata +.. autoclass:: ImageMetadata + :members: read, write, dimensions, mime_type, exif_keys, iptc_keys, + xmp_keys, __getitem__, __setitem__, __delitem__, previews, copy + +pyexiv2.exif +############ + +.. module:: pyexiv2.exif +.. autoexception:: ExifValueError +.. autoclass:: ExifTag + :members: key, type, name, label, description, section_name, + section_description, raw_value, value, human_value + +pyexiv2.iptc +############ + +.. module:: pyexiv2.iptc +.. autoexception:: IptcValueError +.. autoclass:: IptcTag + :members: key, type, name, title, description, photoshop_name, repeatable, + record_name, record_description, raw_values, values + +pyexiv2.xmp +########### + +.. module:: pyexiv2.xmp +.. autoexception:: XmpValueError +.. autoclass:: XmpTag + :members: key, type, name, title, description, raw_value, value + +pyexiv2.utils +############# + +.. module:: pyexiv2.utils +.. autofunction:: undefined_to_string +.. autofunction:: string_to_undefined + +.. autoclass:: Rational + :members: numerator, denominator, from_string, to_float, __eq__ +.. autoclass:: GPSCoordinate + :members: degrees, minutes, seconds, direction, from_string, __eq__, __str__ + diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 0000000..65b7ce0 --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,196 @@ +# -*- coding: utf-8 -*- +# +# pyexiv2 documentation build configuration file, created by +# sphinx-quickstart on Thu Jan 28 20:19:48 2010. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os +import time +import pyexiv2 + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.append(os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'pyexiv2' +copyright = u'2006-%s, Olivier Tilloy' % time.strftime('%Y') + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = pyexiv2._make_version(pyexiv2.version_info[:2]) +# The full version, including alpha/beta/rc tags. +release = pyexiv2.__version__ + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of documents that shouldn't be included in the build. +#unused_docs = [] + +# List of directories, relative to source directory, that shouldn't be searched +# for source files. +exclude_trees = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. Major themes that come with +# Sphinx are currently 'default' and 'sphinxdoc'. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# "<project> v<release> documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_use_modindex = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a <link> tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = '' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'pyexiv2doc' + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'pyexiv2.tex', u'pyexiv2 Documentation', + u'Olivier Tilloy', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_use_modindex = True diff --git a/doc/developers.rst b/doc/developers.rst new file mode 100644 index 0000000..8bf39bd --- /dev/null +++ b/doc/developers.rst @@ -0,0 +1,124 @@ +Developers +========== + +If you are a developer and use pyexiv2 in your project, you will find here +useful information. + +Getting the code +################ + +pyexiv2's source code is versioned with +`bazaar <http://bazaar.canonical.com/>`_, and all the branches, including the +main development focus (sometimes referred to as *trunk*), are hosted on +`Launchpad <https://code.launchpad.net/pyexiv2>`_. + +To get a working copy of the latest revision of the development branch, just +issue the following command in a terminal:: + + bzr branch lp:pyexiv2 + +If you need to get a specific revision identified by a tag (all releases of +pyexiv2 are tagged), use the following command:: + + bzr branch -r tag:tag_name lp:pyexiv2 + +A list of all the available tags can be obtained using the ``bzr tags`` +command:: + + osomon@granuja:~/dev/pyexiv2$ bzr tags + release-0.1 60 + release-0.1.1 73 + release-0.1.2 99 + release-0.1.3 99.1.6 + +Dependencies +############ + +You will need the following dependencies installed on your system to build and +use pyexiv2: + +* `Python <http://python.org/download/>`_ ≥ 2.5 +* `boost.python <http://www.boost.org/libs/python/doc/>`_ ≥ 1.35 +* `libexiv2 <http://exiv2.org/>`_ ≥ 0.19 +* `SCons <http://scons.org/>`_ + +For Python, boost.python and libexiv2, the development files are needed +(-dev packages). +A typical list of packages to install on a Debian/Ubuntu system is:: + + python-all-dev libboost-python-dev libexiv2-dev scons + +Building and installing +####################### + +Building pyexiv2 is as simple as invoking ``scons`` in the top-level directory:: + + osomon@granuja:~/dev/pyexiv2$ scons + scons: Reading SConscript files ... + scons: done reading SConscript files. + scons: Building targets ... + g++ -o build/exiv2wrapper.os -c -fPIC -I/usr/include/python2.6 src/exiv2wrapper.cpp + g++ -o build/exiv2wrapper_python.os -c -fPIC -I/usr/include/python2.6 src/exiv2wrapper_python.cpp + g++ -o build/libexiv2python.so -shared build/exiv2wrapper.os build/exiv2wrapper_python.os -lboost_python -lexiv2 + scons: done building targets. + +The result of the build process is a shared library, ``libexiv2python.so``, in +the build directory:: + + osomon@granuja:~/dev/pyexiv2$ ls build/ + exiv2wrapper.os exiv2wrapper_python.os libexiv2python.so + +To install pyexiv2, just invoke ``scons install``. You will most likely need +administrative privileges to proceed. + +Documentation +############# + +The present documentation is generated using +`Sphinx <http://sphinx.pocoo.org/>`_ from reStructuredText sources found in the +doc/ directory. Invoke ``scons doc`` to (re)build the HTML documentation. +Alternatively, you can issue the following command:: + + sphinx-build -b html doc/ doc/_build/ + +The index of the documentation will then be found under doc/_build/index.html. +Note that you will need pyexiv2 to be installed system-wide or to be found on +the ``PYTHONPATH`` for the documentation to build successfully. + +Unit tests +########## + +pyexiv2's source comes with a battery of unit tests, in the test/ directory. +To run them, ``cd`` to this directory and execute the ``TestsRunner.py`` +script, making sure that pyexiv2 is installed system-wide or can be found on +the ``PYTHONPATH``. + +Contributing +############ + +pyexiv2 is Free Software, meaning that you are encouraged to use it, modify it +to suit your needs, contribute back improvements, and redistribute it. + +`Bugs <https://bugs.launchpad.net/pyexiv2>`_ are tracked on Launchpad. +There is a team called +`pyexiv2-developers <https://launchpad.net/~pyexiv2-developers>`_ open to anyone +interested in following development on pyexiv2. Don't hesitate to subscribe to +the team (you don't need to actually contribute!) and to the associated mailing +list. + +There are several ways in which you can contribute to improve pyexiv2: + +* Use it; +* Give your feedback and discuss issues and feature requests on the + mailing list; +* Report bugs, write patches; +* Package it for your favorite distribution/OS. + +When reporting a bug, don't forget to include the following information in the +report: + +* version of pyexiv2 +* version of libexiv2 it was compiled against +* a minimal script that reliably reproduces the issue +* a sample image file with which the bug can reliably be reproduced + diff --git a/doc/index.rst b/doc/index.rst new file mode 100644 index 0000000..463b364 --- /dev/null +++ b/doc/index.rst @@ -0,0 +1,43 @@ +.. pyexiv2 documentation master file, created by + sphinx-quickstart on Thu Jan 28 20:19:48 2010. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +pyexiv2 documentation +===================== + +Welcome! This is the documentation for pyexiv2 |release|, last updated |today|. + +pyexiv2 is a python binding to `exiv2 <http://exiv2.org/>`_, the C++ library +for manipulation of :abbr:`EXIF (EXchangeable Image File)`, +:abbr:`IPTC (International Press Telecommunications Council)` and +:abbr:`XMP (eXtensible Metadata Platform)` image metadata. +It is a python module that allows your python scripts to read and write +metadata (EXIF, IPTC, XMP, thumbnails) embedded in image files +(JPEG, TIFF, ...). + +It is designed as a high-level interface to the functionalities offered by +libexiv2. Using python's built-in data types and standard modules, it provides +easy manipulation of image metadata. + +pyexiv2 is distributed under the +`GPL version 2 <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>`_ +license. + +Contents: + +.. toctree:: + :maxdepth: 2 + + tutorial.rst + api.rst + developers.rst + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/doc/tutorial.rst b/doc/tutorial.rst new file mode 100644 index 0000000..a5bc849 --- /dev/null +++ b/doc/tutorial.rst @@ -0,0 +1,234 @@ +Tutorial +======== + +This tutorial is meant to give you a quick overview of what pyexiv2 allows you +to do. You can just read it through or follow it interactively, in which case +you will need to have pyexiv2 installed. +It doesn't cover all the possibilities offered by pyexiv2, only a basic subset +of them. For complete reference, see the :doc:`api`. + +Let's get started! + +First of all, we import the pyexiv2 module:: + + >>> import pyexiv2 + +We then load an image file and read its metadata:: + + >>> metadata = pyexiv2.ImageMetadata('test.jpg') + >>> metadata.read() + +Reading and writing EXIF tags +############################# + +Let's retrieve a list of all the available EXIF tags available in the image:: + + >>> metadata.exif_keys + ['Exif.Image.ImageDescription', + 'Exif.Image.XResolution', + 'Exif.Image.YResolution', + 'Exif.Image.ResolutionUnit', + 'Exif.Image.Software', + 'Exif.Image.DateTime', + 'Exif.Image.Artist', + 'Exif.Image.Copyright', + 'Exif.Image.ExifTag', + 'Exif.Photo.Flash', + 'Exif.Photo.PixelXDimension', + 'Exif.Photo.PixelYDimension'] + +Each of those tags can be accessed with the ``[]`` operator on the metadata, +much like a python dictionary:: + + >>> tag = metadata['Exif.Image.DateTime'] + +The value of an :class:`ExifTag` object can be accessed in two different ways: +with the :attr:`raw_value` and with the :attr:`value` attributes:: + + >>> tag.raw_value + '2004-07-13T21:23:44Z' + + >>> tag.value + datetime.datetime(2004, 7, 13, 21, 23, 44) + +The raw value is always a byte string, this is how the value is stored in the +file. The value is lazily computed from the raw value depending on the EXIF type +of the tag, and is represented as a convenient python object to allow easy +manipulation. + +Note that querying the value of a tag may raise an :exc:`ExifValueError` if the +format of the raw value is invalid according to the EXIF specification (may +happen if it was written by other software that implements the specification in +a broken manner), or if pyexiv2 doesn't know how to convert it to a convenient +python object. + +Accessing the value of a tag as a python object allows easy manipulation and +formatting:: + + >>> tag.value.strftime('%A %d %B %Y, %H:%M:%S') + 'Tuesday 13 July 2004, 21:23:44' + +Now let's modify the value of the tag and write it back to the file:: + + >>> import datetime + >>> tag.value = datetime.datetime.today() + + >>> metadata.write() + +Similarly to reading the value of a tag, one can set either the +:attr:`raw_value` or the :attr:`value` (which will be automatically converted to +a correctly formatted byte string by pyexiv2). + +You can also add new tags to the metadata by providing a valid key and value +pair (see exiv2's documentation for a list of valid +`EXIF tags <http://exiv2.org/tags.html>`_):: + + >>> key = 'Exif.Photo.UserComment' + >>> value = 'This is a useful comment.' + >>> metadata[key] = pyexiv2.ExifTag(key, value) + + +Reading and writing IPTC tags +############################# + +Reading and writing IPTC tags works pretty much the same way as with EXIF tags. +Let's retrieve the list of all available IPTC tags in the image:: + + >>> metadata.iptc_keys + ['Iptc.Application2.Caption', + 'Iptc.Application2.Writer', + 'Iptc.Application2.Byline', + 'Iptc.Application2.ObjectName', + 'Iptc.Application2.DateCreated', + 'Iptc.Application2.City', + 'Iptc.Application2.ProvinceState', + 'Iptc.Application2.CountryName', + 'Iptc.Application2.Category', + 'Iptc.Application2.Keywords', + 'Iptc.Application2.Copyright'] + +Each of those tags can be accessed with the ``[]`` operator on the metadata:: + + >>> tag = metadata['Iptc.Application2.DateCreated'] + +An IPTC tag always has a list of values rather than a single value. +This is because some tags have a repeatable character. +Tags that are not repeatable only hold one value in their list of values. + +Check the :attr:`repeatable` attribute to know whether a tag can hold more than +one value:: + + >>> tag.repeatable + False + +As with EXIF tags, the values of an :class:`IptcTag` object can be accessed in +two different ways: with the :attr:`raw_values` and with the :attr:`values` +attributes:: + + >>> tag.raw_values + ['2004-07-13'] + + >>> tag.values + [datetime.date(2004, 7, 13)] + +Note that querying the values of a tag may raise an :exc:`IptcValueError` if the +format of the raw values is invalid according to the IPTC specification (may +happen if it was written by other software that implements the specification in +a broken manner), or if pyexiv2 doesn't know how to convert it to a convenient +python object. + +Now let's modify the values of the tag and write it back to the file:: + + >>> tag.values = [datetime.date.today()] + + >>> metadata.write() + +Similarly to reading the values of a tag, one can set either the +:attr:`raw_values` or the :attr:`values` (which will be automatically converted +to correctly formatted byte strings by pyexiv2). + +You can also add new tags to the metadata by providing a valid key and values +pair (see exiv2's documentation for a list of valid +`IPTC tags <http://exiv2.org/iptc.html>`_):: + + >>> key = 'Iptc.Application2.Contact' + >>> values = ['John', 'Paul', 'Ringo', 'George'] + >>> metadata[key] = pyexiv2.IptcTag(key, values) + +Reading and writing XMP tags +############################ + +Reading and writing XMP tags works pretty much the same way as with EXIF tags. +Let's retrieve the list of all available XMP tags in the image:: + + >>> metadata.xmp_keys + ['Xmp.dc.creator', + 'Xmp.dc.description', + 'Xmp.dc.rights', + 'Xmp.dc.source', + 'Xmp.dc.subject', + 'Xmp.dc.title', + 'Xmp.xmp.CreateDate', + 'Xmp.xmp.ModifyDate'] + +Each of those tags can be accessed with the ``[]`` operator on the metadata:: + + >>> tag = metadata['Xmp.xmp.ModifyDate'] + +As with EXIF tags, the values of an :class:`XmpTag` object can be accessed in +two different ways: with the :attr:`raw_value` and with the :attr:`value` +attributes:: + + >>> tag.raw_value + '2002-07-19T13:28:10' + + >>> tag.value + datetime.datetime(2002, 7, 19, 13, 28, 10) + +Note that querying the value of a tag may raise an :exc:`XmpValueError` if the +format of the raw value is invalid according to the XMP specification (may +happen if it was written by other software that implements the specification in +a broken manner), or if pyexiv2 doesn't know how to convert it to a convenient +python object. + +Now let's modify the value of the tag and write it back to the file:: + + >>> tag.value = datetime.datetime.today() + + >>> metadata.write() + +Similarly to reading the value of a tag, one can set either the +:attr:`raw_value` or the :attr:`value` (which will be automatically converted to +a correctly formatted byte string by pyexiv2). + +You can also add new tags to the metadata by providing a valid key and value +pair (see exiv2's documentation for a list of valid +`XMP tags <http://exiv2.org/tags-xmp-dc.html>`_):: + + >>> key = 'Xmp.xmp.Label' + >>> value = 'A beautiful picture.' + >>> metadata[key] = pyexiv2.XmpTag(key, value) + +Accessing embedded previews +########################### + +Images may embed previews (also called thumbnails) of various sizes in their +metadata. pyexiv2 allows to easily access them:: + + >>> previews = metadata.previews + + >>> len(previews) + 2 + +They are sorted by increasing size. Let's play with the largest one:: + + >>> largest = previews[-1] + + >>> largest.dimensions + (320, 240) + + >>> largest.mime_type + 'image/jpeg' + + >>> largest.write_to_file('largest') + diff --git a/src/SConscript b/src/SConscript index daa4bff..3076c19 100644 --- a/src/SConscript +++ b/src/SConscript @@ -1,4 +1,3 @@ -#!/usr/bin/python # -*- coding: utf-8 -*- import os @@ -26,6 +25,7 @@ env.Append(LIBS=libs) # Build shared library libpyexiv2 cpp_sources = ['exiv2wrapper.cpp', 'exiv2wrapper_python.cpp'] libpyexiv2 = env.SharedLibrary('exiv2python', cpp_sources) +env.Alias('lib', libpyexiv2) # Install the shared library and the Python modules, invoked using # 'scons install'. If DESTDIR is specified on the command line when invoking @@ -40,3 +40,4 @@ else: env.Install(install_dir, [libpyexiv2]) env.Install(os.path.join(install_dir, 'pyexiv2'), Glob('pyexiv2/*.py')) env.Alias('install', install_dir) + diff --git a/src/pyexiv2/__init__.py b/src/pyexiv2/__init__.py index 91ce1b5..5cf82fa 100644 --- a/src/pyexiv2/__init__.py +++ b/src/pyexiv2/__init__.py @@ -72,9 +72,15 @@ def _make_version(version_info): return '.'.join([str(i) for i in version_info]) +#: A tuple containing the three components of the version number: major, minor, micro. version_info = (0, 2, 0) + +#: The version of the module as a string (major.minor.micro). __version__ = _make_version(version_info) +#: A tuple containing the three components of the version number of libexiv2: major, minor, micro. exiv2_version_info = libexiv2python.exiv2_version_info + +#: The version of libexiv2 as a string (major.minor.micro). __exiv2_version__ = _make_version(exiv2_version_info) diff --git a/src/pyexiv2/exif.py b/src/pyexiv2/exif.py index 97a0190..6f2f824 100644 --- a/src/pyexiv2/exif.py +++ b/src/pyexiv2/exif.py @@ -40,12 +40,12 @@ import datetime class ExifValueError(ValueError): """ - Exception raised when failing to parse the value of an EXIF tag. + Exception raised when failing to parse the *value* of an EXIF tag. - @ivar value: the value that fails to be parsed - @type value: C{str} - @ivar type: the EXIF type of the tag - @type type: C{str} + :attribute value: the value that fails to be parsed + :type value: string + :attribute type: the EXIF type of the tag + :type type: string """ def __init__(self, value, type): @@ -64,16 +64,17 @@ class ExifTag(ListenerInterface): Here is a correspondance table between the EXIF types and the possible python types the value of a tag may take: - - Ascii: C{datetime.datetime}, C{datetime.date}, C{str} - - Byte, SByte: C{str} - - Comment: C{str} - - Short, SShort: [list of] C{int} - - Long, SLong: [list of] C{long} - - Rational, SRational: [list of] L{pyexiv2.utils.Rational} - - Undefined: C{str} - - @ivar metadata: the parent metadata if any, or C{None} - @type metadata: L{pyexiv2.metadata.ImageMetadata} + + - Ascii: :class:`datetime.datetime`, :class:`datetime.date`, string + - Byte, SByte: string + - Comment: string + - Long, SLong: [list of] long + - Short, SShort: [list of] int + - Rational, SRational: [list of] :class:`pyexiv2.utils.Rational` + - Undefined: string + + :attribute metadata: the parent metadata if any, or None + :type metadata: :class:`pyexiv2.metadata.ImageMetadata` """ # According to the EXIF specification, the only accepted format for an Ascii @@ -90,9 +91,9 @@ class ExifTag(ListenerInterface): The tag can be initialized with an optional value which expected type depends on the EXIF type of the tag. - @param key: the key of the tag - @type key: C{str} - @param value: the value of the tag + :param key: the key of the tag + :type key: string + :param value: the value of the tag """ super(ExifTag, self).__init__() if _tag is not None: @@ -115,7 +116,8 @@ class ExifTag(ListenerInterface): @property def key(self): - """The key of the tag in the form 'familyName.groupName.tagName'.""" + """The key of the tag in the dotted form + ``familyName.groupName.tagName`` where ``familyName`` = ``exif``.""" return self._tag._getKey() @property @@ -213,15 +215,13 @@ class ExifTag(ListenerInterface): @property def human_value(self): - """A human-readable representation of the value of the tag.""" + """A (read-only) human-readable representation + of the value of the tag.""" return self._tag._getHumanValue() or None - # Implement the ListenerInterface def contents_changed(self): - """ - Implementation of the L{ListenerInterface}. - React on changes to the list of values of the tag. - """ + # Implementation of the ListenerInterface. + # React on changes to the list of values of the tag. # self._value is a list of values and its contents changed. self._set_value(self._value) @@ -229,13 +229,12 @@ class ExifTag(ListenerInterface): """ Convert one raw value to its corresponding python type. - @param value: the raw value to be converted - @type value: C{str} + :param value: the raw value to be converted + :type value: string - @return: the value converted to its corresponding python type - @rtype: depends on C{self.type} + :return: the value converted to its corresponding python type - @raise ExifValueError: if the conversion fails + :raise ExifValueError: if the conversion fails """ if self.type == 'Ascii': # The value may contain a Datetime @@ -304,13 +303,12 @@ class ExifTag(ListenerInterface): Convert one value to its corresponding string representation, suitable to pass to libexiv2. - @param value: the value to be converted - @type value: depends on C{self.type} + :param value: the value to be converted - @return: the value converted to its corresponding string representation - @rtype: C{str} + :return: the value converted to its corresponding string representation + :rtype: string - @raise ExifValueError: if the conversion fails + :raise ExifValueError: if the conversion fails """ if self.type == 'Ascii': if type(value) is datetime.datetime: @@ -404,9 +402,8 @@ class ExifTag(ListenerInterface): def __str__(self): """ - Return a string representation of the EXIF tag for debugging purposes. - - @rtype: C{str} + :return: a string representation of the EXIF tag for debugging purposes + :rtype: string """ left = '%s [%s]' % (self.key, self.type) if self._raw_value is None: diff --git a/src/pyexiv2/iptc.py b/src/pyexiv2/iptc.py index 35bd77c..53d82dc 100644 --- a/src/pyexiv2/iptc.py +++ b/src/pyexiv2/iptc.py @@ -40,12 +40,12 @@ import re class IptcValueError(ValueError): """ - Exception raised when failing to parse the value of an IPTC tag. + Exception raised when failing to parse the *value* of an IPTC tag. - @ivar value: the value that fails to be parsed - @type value: C{str} - @ivar type: the IPTC type of the tag - @type type: C{str} + :attribute value: the value that fails to be parsed + :type value: string + :attribute type: the IPTC type of the tag + :type type: string """ def __init__(self, value, type): @@ -62,18 +62,19 @@ class IptcTag(ListenerInterface): """ An IPTC tag. - This tag can have several values (tags that have the repeatable property). + This tag can have several values (tags that have the *repeatable* property). Here is a correspondance table between the IPTC types and the possible python types the value of a tag may take: - - Short: C{int} - - String: C{str} - - Date: C{datetime.date} - - Time: C{datetime.time} - - Undefined: C{str} - - @ivar metadata: the parent metadata if any, or C{None} - @type metadata: L{pyexiv2.metadata.ImageMetadata} + + - Short: int + - String: string + - Date: :class:`datetime.date` + - Time: :class:`datetime.time` + - Undefined: string + + :attribute metadata: the parent metadata if any, or None + :type metadata: :class:`pyexiv2.metadata.ImageMetadata` """ # strptime is not flexible enough to handle all valid Time formats, we use a @@ -86,10 +87,9 @@ class IptcTag(ListenerInterface): The tag can be initialized with an optional list of values which expected type depends on the IPTC type of the tag. - @param key: the key of the tag - @type key: C{str} - @param values: the values of the tag - @type values: C{list} + :param key: the key of the tag + :type key: string + :param values: the values of the tag """ super(IptcTag, self).__init__() if _tag is not None: @@ -112,7 +112,8 @@ class IptcTag(ListenerInterface): @property def key(self): - """The key of the tag in the form 'familyName.groupName.tagName'.""" + """The key of the tag in the dotted form + ``familyName.groupName.tagName`` where ``familyName`` = ``iptc``.""" return self._tag._getKey() @property @@ -143,7 +144,7 @@ class IptcTag(ListenerInterface): @property def repeatable(self): - """Whether the tag is repeatable (can have several values).""" + """Whether the tag is repeatable (accepts several values).""" return self._tag._isRepeatable() @property @@ -205,10 +206,8 @@ class IptcTag(ListenerInterface): doc='The values of the tag as a list of python objects.') def contents_changed(self): - """ - Implementation of the L{ListenerInterface}. - React on changes to the list of values of the tag. - """ + # Implementation of the ListenerInterface. + # React on changes to the list of values of the tag. # The contents of self._values was changed. # The following is a quick, non optimal solution. self._set_values(self._values) @@ -217,13 +216,12 @@ class IptcTag(ListenerInterface): """ Convert one raw value to its corresponding python type. - @param value: the raw value to be converted - @type value: C{str} + :param value: the raw value to be converted + :type value: string - @return: the value converted to its corresponding python type - @rtype: depends on C{self.type} + :return: the value converted to its corresponding python type - @raise IptcValueError: if the conversion fails + :raise IptcValueError: if the conversion fails """ if self.type == 'Short': try: @@ -280,13 +278,12 @@ class IptcTag(ListenerInterface): Convert one value to its corresponding string representation, suitable to pass to libexiv2. - @param value: the value to be converted - @type value: depends on C{self.type} + :param value: the value to be converted - @return: the value converted to its corresponding string representation - @rtype: C{str} + :return: the value converted to its corresponding string representation + :rtype: string - @raise IptcValueError: if the conversion fails + :raise IptcValueError: if the conversion fails """ if self.type == 'Short': if type(value) is int: @@ -337,9 +334,8 @@ class IptcTag(ListenerInterface): def __str__(self): """ - Return a string representation of the IPTC tag for debugging purposes. - - @rtype: C{str} + :return: a string representation of the IPTC tag for debugging purposes + :rtype: string """ left = '%s [%s]' % (self.key, self.type) if self._raw_values is None: diff --git a/src/pyexiv2/metadata.py b/src/pyexiv2/metadata.py index f868096..82fe1f9 100644 --- a/src/pyexiv2/metadata.py +++ b/src/pyexiv2/metadata.py @@ -46,16 +46,13 @@ class ImageMetadata(object): It provides convenient methods for the manipulation of EXIF, IPTC and XMP metadata embedded in image files such as JPEG and TIFF files, using Python types. - It also provides access to the thumbnails embedded in an image. - - @ivar filename: path to the image file - @type filename: C{str} or C{unicode} + It also provides access to the previews embedded in an image. """ def __init__(self, filename): """ - @param filename: path to an image file - @type filename: C{str} or C{unicode} + :param filename: path to an image file + :type filename: string """ self.filename = filename if isinstance(filename, unicode): @@ -90,31 +87,32 @@ class ImageMetadata(object): @property def dimensions(self): - """The width and height of the image, expressed in pixels.""" + """A tuple containing the width and height of the image, expressed in + pixels.""" return (self._image._getPixelWidth(), self._image._getPixelHeight()) @property def mime_type(self): - """The mime type of the image.""" + """The mime type of the image, as a string.""" return self._image._getMimeType() @property def exif_keys(self): - """Keys of the available EXIF tags embedded in the image.""" + """List of the keys of the available EXIF tags.""" if self._keys['exif'] is None: self._keys['exif'] = self._image._exifKeys() return self._keys['exif'] @property def iptc_keys(self): - """Keys of the available IPTC tags embedded in the image.""" + """List of the keys of the available IPTC tags.""" if self._keys['iptc'] is None: self._keys['iptc'] = self._image._iptcKeys() return self._keys['iptc'] @property def xmp_keys(self): - """Keys of the available XMP tags embedded in the image.""" + """List of the keys of the available XMP tags.""" if self._keys['xmp'] is None: self._keys['xmp'] = self._image._xmpKeys() return self._keys['xmp'] @@ -159,14 +157,12 @@ class ImageMetadata(object): """ Get a metadata tag for a given key. - @param key: a metadata key in the dotted form - C{familyName.groupName.tagName} where C{familyName} may be - C{exif}, C{iptc} or C{xmp}. - @type key: C{str} - - @return: the metadata tag corresponding to the key + :param key: metadata key in the dotted form + ``familyName.groupName.tagName`` where ``familyName`` may + be one of ``exif``, ``iptc`` or ``xmp``. + :type key: string - @raise KeyError: if the tag doesn't exist + :raise KeyError: if the tag doesn't exist """ family = key.split('.')[0].lower() try: @@ -262,13 +258,15 @@ class ImageMetadata(object): Set a metadata tag for a given key. If the tag was previously set, it is overwritten. - @param key: a metadata key in the dotted form - C{familyName.groupName.tagName} where C{familyName} may be - C{exif}, C{iptc} or C{xmp}. - @type key: C{str} - @param tag: a metadata tag + :param key: metadata key in the dotted form + ``familyName.groupName.tagName`` where ``familyName`` may + be one of ``exif``, ``iptc`` or ``xmp``. + :type key: string + :param tag: an instance of the corresponding family of metadata tag + :type tag: :class:`pyexiv2.exif.ExifTag` or + :class:`pyexiv2.iptc.IptcTag` or :class:`pyexiv2.xmp.XmpTag` - @raise KeyError: if the key is invalid + :raise KeyError: if the key is invalid """ family = key.split('.')[0].lower() try: @@ -316,12 +314,12 @@ class ImageMetadata(object): """ Delete a metadata tag for a given key. - @param key: a metadata key in the dotted form - C{familyName.groupName.tagName} where C{familyName} may be - C{exif}, C{iptc} or C{xmp}. - @type key: C{str} + :param key: metadata key in the dotted form + ``familyName.groupName.tagName`` where ``familyName`` may + be one of ``exif``, ``iptc`` or ``xmp``. + :type key: string - @raise KeyError: if the tag with the given key doesn't exist + :raise KeyError: if the tag with the given key doesn't exist """ family = key.split('.')[0].lower() try: @@ -340,16 +338,17 @@ class ImageMetadata(object): Copy the metadata to another image. The metadata in the destination is overridden. In particular, if the destination contains e.g. EXIF data and the source doesn't, it will be - erased in the destination, unless explicitely omitted. - - @param other: the destination metadata to copy to - @type other: L{pyexiv2.ImageMetadata} - @param exif: whether to copy the EXIF metadata (C{True} by default) - @type exif: C{bool} - @param iptc: whether to copy the IPTC metadata (C{True} by default) - @type iptc: C{bool} - @param xmp: whether to copy the XMP metadata (C{True} by default) - @type xmp: C{bool} + erased in the destination, unless explicitly omitted. + + :param other: the destination metadata to copy to (it must have been + :meth:`.read` beforehand) + :type other: :class:`pyexiv2.metadata.ImageMetadata` + :param exif: whether to copy the EXIF metadata + :type exif: boolean + :param iptc: whether to copy the IPTC metadata + :type iptc: boolean + :param xmp: whether to copy the XMP metadata + :type xmp: boolean """ self._image._copyMetadata(other._image, exif, iptc, xmp) # Empty the cache where needed diff --git a/src/pyexiv2/utils.py b/src/pyexiv2/utils.py index 6f438fe..573e4c7 100644 --- a/src/pyexiv2/utils.py +++ b/src/pyexiv2/utils.py @@ -37,13 +37,12 @@ class FixedOffset(datetime.tzinfo): """ Fixed positive or negative offset from a local time east from UTC. - @ivar sign: the sign of the offset ('+' or '-') - @type sign: C{str} - @ivar hours: the absolute number of hours of the offset - @type hours: C{int} - @ivar minutes: the absolute number of minutes of the offset - @type minutes: C{int} - + :attribute sign: the sign of the offset ('+' or '-') + :type sign: string + :attribute hours: the absolute number of hours of the offset + :type hours: int + :attribute minutes: the absolute number of minutes of the offset + :type minutes: int """ def __init__(self, sign='+', hours=0, minutes=0): @@ -53,12 +52,12 @@ class FixedOffset(datetime.tzinfo): No check on the validity of those values is performed, it is the responsibility of the caller to pass valid values. - @param sign: the sign of the offset ('+' or '-') - @type sign: C{str} - @param hours: an absolute number of hours - @type hours: C{int} - @param minutes: an absolute number of minutes - @type minutes: C{int} + :param sign: the sign of the offset ('+' or '-') + :type sign: string + :param hours: an absolute number of hours + :type hours: int + :param minutes: an absolute number of minutes + :type minutes: int """ self.sign = sign self.hours = hours @@ -69,11 +68,11 @@ class FixedOffset(datetime.tzinfo): Return offset of local time from UTC, in minutes east of UTC. If local time is west of UTC, this value will be negative. - @param dt: the local time - @type dt: C{datetime.time} + :param dt: the local time + :type dt: :class:`datetime.time` - @return: a whole number of minutes in the range -1439 to 1439 inclusive - @rtype: C{datetime.timedelta} + :return: a whole number of minutes in the range -1439 to 1439 inclusive + :rtype: :class:`datetime.timedelta` """ total = self.hours * 60 + self.minutes if self.sign == '-': @@ -85,11 +84,11 @@ class FixedOffset(datetime.tzinfo): Return the daylight saving time (DST) adjustment. In this implementation, it is always nil. - @param dt: the local time - @type dt: C{datetime.time} + :param dt: the local time + :type dt: :class:`datetime.time` - @return: the DST adjustment (always nil) - @rtype: C{datetime.timedelta} + :return: the DST adjustment (always nil) + :rtype: :class:`datetime.timedelta` """ return datetime.timedelta(0) @@ -98,11 +97,11 @@ class FixedOffset(datetime.tzinfo): Return a string representation of the offset in the format '±%H:%M'. If the offset is nil, the representation is, by convention, 'Z'. - @param dt: the local time - @type dt: C{datetime.time} + :param dt: the local time + :type dt: :class:`datetime.time` - @return: a human-readable representation of the offset - @rtype: C{str} + :return: a human-readable representation of the offset + :rtype: string """ if self.hours == 0 and self.minutes == 0: return 'Z' @@ -113,11 +112,11 @@ class FixedOffset(datetime.tzinfo): """ Test equality between this offset and another offset. - @param other: another offset - @type other: L{FixedOffset} + :param other: another offset + :type other: :class:`FixedOffset` - @return: C{True} if the offset are equal, C{False} otherwise - @rtype: C{bool} + :return: True if the offset are equal, False otherwise + :rtype: boolean """ return (self.sign == other.sign) and (self.hours == other.hours) and \ (self.minutes == other.minutes) @@ -131,11 +130,11 @@ def undefined_to_string(undefined): "0221"). The Undefined type is part of the EXIF specification. - @param undefined: an undefined string - @type undefined: C{str} + :param undefined: an undefined string + :type undefined: string - @return: the corresponding decoded string - @rtype: C{str} + :return: the corresponding decoded string + :rtype: string """ return ''.join(map(lambda x: chr(int(x)), undefined.rstrip().split(' '))) @@ -147,11 +146,11 @@ def string_to_undefined(sequence): blank space (e.g. "0221" will be converted into "48 50 50 49 "). The Undefined type is part of the EXIF specification. - @param sequence: a sequence of bytes - @type sequence: C{str} + :param sequence: a sequence of bytes + :type sequence: string - @return: the corresponding undefined string - @rtype: C{str} + :return: the corresponding undefined string + :rtype: string """ return ''.join(map(lambda x: '%d ' % ord(x), sequence)) @@ -168,14 +167,12 @@ class Rational(object): def __init__(self, numerator, denominator): """ - Constructor. - - @param numerator: the numerator - @type numerator: C{long} - @param denominator: the denominator - @type denominator: C{long} + :param numerator: the numerator + :type numerator: long + :param denominator: the denominator + :type denominator: long - @raise ZeroDivisionError: if the denominator equals zero + :raise ZeroDivisionError: if the denominator equals zero """ if denominator == 0: msg = 'Denominator of a rational number cannot be zero.' @@ -196,16 +193,16 @@ class Rational(object): @staticmethod def from_string(string): """ - Instantiate a Rational from a string formatted as - C{[-]numerator/denominator}. + Instantiate a :class:`Rational` from a string formatted as + ``[-]numerator/denominator``. - @param string: a string representation of a rational number - @type string: C{str} + :param string: a string representation of a rational number + :type string: string - @return: the rational number parsed - @rtype: L{Rational} + :return: the rational number parsed + :rtype: :class:`Rational` - @raise ValueError: if the format of the string is invalid + :raise ValueError: if the format of the string is invalid """ match = Rational._format_re.match(string) if match is None: @@ -215,8 +212,8 @@ class Rational(object): def to_float(self): """ - @return: a floating point number approximation of the value - @rtype: C{float} + :return: a floating point number approximation of the value + :rtype: float """ return float(self._numerator) / self._denominator @@ -226,18 +223,19 @@ class Rational(object): Two rational numbers are equal if their reduced forms are equal. - @param other: the rational number to compare to self for equality - @type other: L{Rational} - - @return: C{True} if equal, C{False} otherwise - @rtype: C{bool} + :param other: the rational number to compare to self for equality + :type other: :class:`Rational` + + :return: True if equal, False otherwise + :rtype: boolean """ return (self._numerator * other._denominator) == \ (other._numerator * self._denominator) def __str__(self): """ - Return a string representation of the rational number. + :return: a string representation of the rational number + :rtype: string """ return '%d/%d' % (self._numerator, self._denominator) @@ -262,7 +260,8 @@ class NotifyingList(list): """ A simplistic implementation of a notifying list. Any changes to the list are notified in a synchronous way to all previously - registered listeners. A listener must implement the L{ListenerInterface}. + registered listeners. A listener must implement the + :class:`ListenerInterface`. """ # Useful documentation: @@ -277,8 +276,8 @@ class NotifyingList(list): """ Register a new listener to be notified of changes. - @param listener: any object that listens for changes - @type listener: any class that implements the L{ListenerInterface} + :param listener: any object that listens for changes + :type listener: :class:`ListenerInterface` """ self._listeners.add(listener) @@ -286,10 +285,10 @@ class NotifyingList(list): """ Unregister a previously registered listener. - @param listener: a previously registered listener - @type listener: any class that implements the L{ListenerInterface} + :param listener: a previously registered listener + :type listener: :class:`ListenerInterface` - @raise KeyError: if the listener was not previously registered + :raise KeyError: if the listener was not previously registered """ self._listeners.remove(listener) @@ -378,16 +377,17 @@ class GPSCoordinate(object): def __init__(self, degrees, minutes, seconds, direction): """ - Constructor. + :param degrees: degrees + :type degrees: int + :param minutes: minutes + :type minutes: int + :param seconds: seconds + :type seconds: int + :param direction: direction ('N', 'S', 'E' or 'W') + :type direction: string - @param degrees: degrees - @type degrees: C{int} - @param minutes: minutes - @type minutes: C{int} - @param seconds: seconds - @type seconds: C{int} - @param direction: direction ('N', 'S', 'E' or 'W') - @type direction: C{str} + :raise ValueError: if any of the parameter is not in the expected range + of values """ if direction not in ('N', 'S', 'E', 'W'): raise ValueError('Invalid direction: %s' % direction) @@ -426,19 +426,19 @@ class GPSCoordinate(object): @staticmethod def from_string(string): """ - Instantiate a GPSCoordinate from a string formatted as C{DDD,MM,SSk} or - C{DDD,MM.mmk} where C{DDD} is a number of degrees, C{MM} is a number of - minutes, C{SS} is a number of seconds, C{mm} is a fraction of minutes, - and C{k} is a single character C{N}, C{S}, C{E}, or C{W} indicating a - direction (north, south, east, west). + Instantiate a :class:`GPSCoordinate` from a string formatted as + ``DDD,MM,SSk`` or ``DDD,MM.mmk`` where ``DDD`` is a number of degrees, + ``MM`` is a number of minutes, ``SS`` is a number of seconds, ``mm`` is + a fraction of minutes, and ``k`` is a single character N, S, E, W + indicating a direction (north, south, east, west). - @param string: a string representation of a GPS coordinate - @type string: C{str} + :param string: a string representation of a GPS coordinate + :type string: string - @return: the GPS coordinate parsed - @rtype: L{GPSCoordinate} + :return: the GPS coordinate parsed + :rtype: :class:`GPSCoordinate` - @raise ValueError: if the format of the string is invalid + :raise ValueError: if the format of the string is invalid """ match = GPSCoordinate._format_re.match(string) if match is None: @@ -458,11 +458,11 @@ class GPSCoordinate(object): Two coordinates are equal if and only if all their components are equal. - @param other: the GPS coordinate to compare to self for equality - @type other: L{GPSCoordinate} + :param other: the GPS coordinate to compare to self for equality + :type other: :class:`GPSCoordinate` - @return: C{True} if equal, C{False} otherwise - @rtype: C{bool} + :return: True if equal, False otherwise + :rtype: boolean """ return (self._degrees == other._degrees) and \ (self._minutes == other._minutes) and \ @@ -471,8 +471,9 @@ class GPSCoordinate(object): def __str__(self): """ - Return a string representation of the GPS coordinate conforming to the - XMP specification. + :return: a string representation of the GPS coordinate conforming to the + XMP specification + :rtype: string """ return '%d,%d,%d%s' % (self._degrees, self._minutes, self._seconds, self._direction) diff --git a/src/pyexiv2/xmp.py b/src/pyexiv2/xmp.py index fceac71..f4954d5 100644 --- a/src/pyexiv2/xmp.py +++ b/src/pyexiv2/xmp.py @@ -39,12 +39,12 @@ import re class XmpValueError(ValueError): """ - Exception raised when failing to parse the value of an XMP tag. + Exception raised when failing to parse the *value* of an XMP tag. - @ivar value: the value that fails to be parsed - @type value: C{str} - @ivar type: the XMP type of the tag - @type type: C{str} + :attribute value: the value that fails to be parsed + :type value: string + :attribute type: the XMP type of the tag + :type type: string """ def __init__(self, value, type): self.value = value @@ -62,26 +62,27 @@ class XmpTag(object): Here is a correspondance table between the XMP types and the possible python types the value of a tag may take: - - alt, bag, seq: C{list} of the contained simple type - - lang alt: C{dict} of (language-code: value) - - Boolean: C{bool} - - Colorant: [not implemented yet] - - Date: C{datetime.date}, C{datetime.datetime} - - Dimensions: [not implemented yet] - - Font: [not implemented yet] - - GPSCoordinate: L{pyexiv2.utils.GPSCoordinate} - - Integer: C{int} - - Locale: [not implemented yet] - - MIMEType: C{tuple} - - Rational: L{pyexiv2.utils.Rational} - - Real: [not implemented yet] - - AgentName, ProperName, Text: C{unicode} - - Thumbnail: [not implemented yet] - - URI, URL: C{str} - - XPath: [not implemented yet] - - @ivar metadata: the parent metadata if any, or C{None} - @type metadata: L{pyexiv2.metadata.ImageMetadata} + + - alt, bag, seq: list of the contained simple type + - lang alt: dict of (language-code: value) + - Boolean: boolean + - Colorant: *[not implemented yet]* + - Date: :class:`datetime.date`, :class:`datetime.datetime` + - Dimensions: *[not implemented yet]* + - Font: *[not implemented yet]* + - GPSCoordinate: :class:`pyexiv2.utils.GPSCoordinate` + - Integer: int + - Locale: *[not implemented yet]* + - MIMEType: 2-tuple of strings + - Rational: :class:`pyexiv2.utils.Rational` + - Real: *[not implemented yet]* + - AgentName, ProperName, Text: unicode string + - Thumbnail: *[not implemented yet]* + - URI, URL: string + - XPath: *[not implemented yet]* + + :attribute metadata: the parent metadata if any, or None + :type metadata: :class:`pyexiv2.metadata.ImageMetadata` """ # FIXME: should inherit from ListenerInterface and implement observation of @@ -98,9 +99,9 @@ class XmpTag(object): The tag can be initialized with an optional value which expected type depends on the XMP type of the tag. - @param key: the key of the tag - @type key: C{str} - @param value: the value of the tag + :param key: the key of the tag + :type key: string + :param value: the value of the tag """ super(XmpTag, self).__init__() if _tag is not None: @@ -129,7 +130,8 @@ class XmpTag(object): @property def key(self): - """The key of the tag in the form 'familyName.groupName.tagName'.""" + """The key of the tag in the dotted form + ``familyName.groupName.tagName`` where ``familyName`` = ``xmp``.""" return self._tag._getKey() @property @@ -237,15 +239,14 @@ class XmpTag(object): """ Convert a raw value to its corresponding python type. - @param value: the raw value to be converted - @type value: C{str} - @param type: the simple type of the raw value - @type type: C{str} + :param value: the raw value to be converted + :type value: string + :param type: the simple type of the raw value + :type type: string - @return: the value converted to its corresponding python type - @rtype: depends on C{type} + :return: the value converted to its corresponding python type - @raise XmpValueError: if the conversion fails + :raise XmpValueError: if the conversion fails """ if type == 'Boolean': if value == 'True': @@ -368,15 +369,14 @@ class XmpTag(object): Convert a value to its corresponding string representation, suitable to pass to libexiv2. - @param value: the value to be converted - @type value: depends on C{type} - @param type: the simple type of the value - @type type: C{str} + :param value: the value to be converted + :param type: the simple type of the value + :type type: string - @return: the value converted to its corresponding string representation - @rtype: C{str} + :return: the value converted to its corresponding string representation + :rtype: string - @raise XmpValueError: if the conversion fails + :raise XmpValueError: if the conversion fails """ if type == 'Boolean': if isinstance(value, bool): @@ -443,9 +443,8 @@ class XmpTag(object): def __str__(self): """ - Return a string representation of the XMP tag for debugging purposes. - - @rtype: C{str} + :return: a string representation of the XMP tag for debugging purposes + :rtype: string """ left = '%s [%s]' % (self.key, self.type) if self._raw_value is None: |