aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOlivier Tilloy <olivier@tilloy.net>2010-02-16 17:10:07 +0100
committerOlivier Tilloy <olivier@tilloy.net>2010-02-16 17:10:07 +0100
commit6f165359c2e67e35b328fdb5b6ad6aa8f22cdab9 (patch)
tree42ef9134b7a9386680eab1d5bb6e3e76d539470e
parentb2b0165fa924426fa8bb17a61c7f655fd5ccac7e (diff)
parent871d24f9d959fe5cf9664bbb4f00ea94ff9b58f0 (diff)
downloadpyexiv2-6f165359c2e67e35b328fdb5b6ad6aa8f22cdab9.tar.gz
Complete documentation generated with sphinx.
-rw-r--r--.bzrignore1
-rw-r--r--SConstruct19
-rw-r--r--doc/SConscript29
-rw-r--r--doc/api.rst58
-rw-r--r--doc/conf.py196
-rw-r--r--doc/developers.rst124
-rw-r--r--doc/index.rst43
-rw-r--r--doc/tutorial.rst234
-rw-r--r--src/SConscript3
-rw-r--r--src/pyexiv2/__init__.py6
-rw-r--r--src/pyexiv2/exif.py73
-rw-r--r--src/pyexiv2/iptc.py70
-rw-r--r--src/pyexiv2/metadata.py77
-rw-r--r--src/pyexiv2/utils.py181
-rw-r--r--src/pyexiv2/xmp.py91
15 files changed, 952 insertions, 253 deletions
diff --git a/.bzrignore b/.bzrignore
index 35ff099..197ca21 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -1,2 +1,3 @@
.sconsign.dblite
build
+doc/_build/*
diff --git a/SConstruct b/SConstruct
index 8e92fb6..9a098df 100644
--- a/SConstruct
+++ b/SConstruct
@@ -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: