diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 18:07:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 18:07:41 +0000 |
commit | 76926159194e180003aa78de97e5f287bf4325a5 (patch) | |
tree | 2cea7245cdc3f66355900c820c145eba90598766 /doc/source/tutorials | |
parent | Initial commit. (diff) | |
download | python-apt-76926159194e180003aa78de97e5f287bf4325a5.tar.xz python-apt-76926159194e180003aa78de97e5f287bf4325a5.zip |
Adding upstream version 2.7.6.upstream/2.7.6
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'doc/source/tutorials')
-rw-r--r-- | doc/source/tutorials/apt-cdrom.rst | 156 | ||||
-rw-r--r-- | doc/source/tutorials/apt-get.rst | 46 | ||||
-rw-r--r-- | doc/source/tutorials/contributing.rst | 315 | ||||
-rw-r--r-- | doc/source/tutorials/index.rst | 8 |
4 files changed, 525 insertions, 0 deletions
diff --git a/doc/source/tutorials/apt-cdrom.rst b/doc/source/tutorials/apt-cdrom.rst new file mode 100644 index 0000000..7e1d794 --- /dev/null +++ b/doc/source/tutorials/apt-cdrom.rst @@ -0,0 +1,156 @@ +Writing your own apt-cdrom +========================== +:Author: Julian Andres Klode <jak@debian.org> +:Release: |release| +:Date: |today| + +This article explains how to utilise python-apt to build your own clone of the +:command:`apt-cdrom` command. To do this, we will take a look at the +:mod:`apt.cdrom` and :mod:`apt.progress.text` modules, and we will learn how +to use apt_pkg.parse_commandline to parse commandline arguments. The code shown +here works on Python 2 and Python 3. + +Basics +------ +The first step in building your own :command:`apt-cdrom` clone is to import the +:mod:`apt` package, which will import :mod:`apt.cdrom` and +:mod:`apt.progress.text`:: + + import apt + +Now we have to create a new :class:`apt.cdrom.Cdrom` object and pass to it an +:class:`apt.progress.text.CdromProgress` object, which is responsible for +displaying the progress and asking questions:: + + cdrom = apt.Cdrom(apt.progress.text.CdromProgress()) + +Now we have to choose the action, depending on the given options on the +command line. For now, we simply use the value of ``sys.argv[1]``:: + + import sys + if sys.argv[1] == 'add': + cdrom.add() + elif sys.argv[1] == 'ident': + cdrom.ident() + +Now we have a basic :command:`apt-cdrom` clone which can add and identify +CD-ROMs:: + + import sys + + import apt + + cdrom = apt.Cdrom(apt.progress.text.CdromProgress()) + if sys.argv[1] == 'add': + cdrom.add() + elif sys.argv[1] == 'ident': + cdrom.ident() + +Advanced example with command-line parsing +------------------------------------------- +Our example clearly misses a way to parse the commandline in a correct +manner. Luckily, :mod:`apt_pkg` provides us with a function to do this: +:func:`apt_pkg.parse_commandline`. To use it, we add ``import apt_pkg`` right +after import apt:: + + import sys + + import apt_pkg + import apt + + +:func:`apt_pkg.parse_commandline` is similar to :mod:`getopt` functions, it +takes a list of recognized options and the arguments and returns all unknown +arguments. If it encounters an unknown argument which starts with a leading +'-', the function raises an error indicating that the option is unknown. The +major difference is that this function manipulates the apt configuration space. + +The function takes 3 arguments. The first argument is an +:class:`apt_pkg.Configuration` object. The second argument is a list of tuples +of the form ``(shortopt, longopt, config, type)``, whereas *shortopt* is a +character indicating the short option name, *longopt* a string indicating the +corresponding long option (e.g. ``"--help"``), *config* the name of the +configuration item which should be set and *type* the type of the argument. + +For apt-cdrom, we can use the following statement:: + + arguments = apt_pkg.parse_commandline(apt_pkg.config, + [('h', "help", "help"), + ('v', "version", "version"), + ('d', "cdrom", "Acquire::cdrom::mount", "HasArg"), + ('r', "rename", "APT::CDROM::Rename"), + ('m', "no-mount", "APT::CDROM::NoMount"), + ('f', "fast", "APT::CDROM::Fast"), + ('n', "just-print", "APT::CDROM::NoAct"), + ('n', "recon", "APT::CDROM::NoAct"), + ('n', "no-act", "APT::CDROM::NoAct"), + ('a', "thorough", "APT::CDROM::Thorough"), + ('c', "config-file", "", "ConfigFile"), + ('o', "option", "", "ArbItem")], args) + + +This allows us to support all options supported by apt-cdrom. The first option +is --help. As you can see, it omits the fourth field of the tuple; which means +it is a boolean argument. Afterwards you could use +``apt_pkg.config.find_b("help")`` to see whether ``--help`` was specified. In +``('d',"cdrom","Acquire::cdrom::mount","HasArg")`` the fourth field is +``"HasArg"``. This means that the option has an argument, in this case the +location of the mount point. ``('c',"config-file","","ConfigFile")`` shows how +to include configuration files. This option takes a parameter which points to +a configuration file which will be added to the configuration space. +``('o',"option","","ArbItem")`` is yet another type of option, which allows users +to set configuration options on the commandline. + +Now we have to check whether help or version is specified, and print a message +and exit afterwards. To do this, we use :meth:`apt_pkg.Configuration.find_b` +which returns ``True`` if the configuration option exists and evaluates to +``True``:: + + if apt_pkg.config.find_b("help"): + print("This should be a help message") + sys.exit(0) + elif apt_pkg.config.find_b("version"): + print("Version blah.") + sys.exit(0) + + +Now we are ready to create our progress object and our cdrom object. Instead +of using :class:`apt.Cdrom` like in the first example, we will use +:class:`apt_pkg.Cdrom` which provides a very similar interface. We could also +use :class:`apt.Cdrom`, but `apt.Cdrom` provides options like *nomount* which +conflict with our commandline parsing:: + + progress = apt.progress.text.CdromProgress() + cdrom = apt_pkg.Cdrom() + + +Now we have to do the action requested by the user on the commandline. To see +which option was requested, we check the list ``arguments`` which was returned +by ``apt_pkg.parse_commandline`` above, and afterwards call ``cdrom.add`` or +``cdrom.ident``:: + + if apt_pkg.config.find_b("help"): + print("This should be a help message") + sys.exit(0) + elif apt_pkg.config.find_b("version"): + print("Version blah.") + sys.exit(0) + + if not arguments: + sys.stderr.write('E: No operation specified\n') + sys.exit(1) + elif arguments[0] == 'add': + cdrom.add(progress) + elif arguments[0] == 'ident': + cdrom.ident(progress) + else: + sys.stderr.write('E: Invalid operation %s\n' % arguments[0]) + sys.exit(1) + + +After putting all our actions into a main() function, we get a completely +working apt-cdrom clone, which just misses useful ``--help`` and ``--version`` +options. If we add a function show_help(), we get an even more complete +apt-cdrom clone: + +.. literalinclude:: ../examples/apt-cdrom.py diff --git a/doc/source/tutorials/apt-get.rst b/doc/source/tutorials/apt-get.rst new file mode 100644 index 0000000..26ebc3d --- /dev/null +++ b/doc/source/tutorials/apt-get.rst @@ -0,0 +1,46 @@ +Doing stuff :command:`apt-get` does +=================================== +:Author: Julian Andres Klode <jak@debian.org> +:Release: |release| +:Date: |today| + +The following article will show how you can use python-apt to do actions done +by the :command:`apt-get` command. + + +Printing the URIs of all index files +------------------------------------ +We all now that we can print the URIs of all our index files by running a +simple ``apt-get --print-uris update``. We can do the same. Responsible for +the source entries is the class :class:`apt_pkg.SourceList`, which can be +combined with an :class:`apt_pkg.Acquire` object using :meth:`get_indexes`. + +First of all, we have to create the objects:: + + acquire = apt_pkg.Acquire() + slist = apt_pkg.SourceList() + +Now we have to parse /etc/apt/sources.list and its friends, by using +:meth:`apt_pkg.SourceList.read_main_list`:: + + slist.read_main_list() + +The **slist** object now knows about the location of the indexes. We now have +to load those indexes into the *acquire* object by calling +:meth:`apt_pkg.SourceList.get_indexes`:: + + slist.get_indexes(acquire, True) + +The first argument is the acquire object into which we will load these indexes, +and the second argument means that we want to fetch all indexes. Now the only +thing left to do is iterating over the list of items and printing out their +URIs. Luckily, there is :attr:`apt_pkg.Acquire.items` which allows us to +iterate over the items:: + + for item in acquire.items: + print(item.desc_uri) + +In the end a program could look like this: + +.. literalinclude:: ../examples/update-print-uris.py + diff --git a/doc/source/tutorials/contributing.rst b/doc/source/tutorials/contributing.rst new file mode 100644 index 0000000..33f1654 --- /dev/null +++ b/doc/source/tutorials/contributing.rst @@ -0,0 +1,315 @@ +Contributing to python-apt +========================== +:Author: Julian Andres Klode <jak@debian.org> +:Release: |release| +:Date: |today| + +Let's say you need a new feature, you can develop it, and you want to get it +included in python-apt. Then be sure to follow the following guidelines. + +Available branches +------------------- +First of all, let's talk a bit about the git branches of python-apt. In the +following parts, we will assume that you use git to create your changes and +submit them. + +Repositories +^^^^^^^^^^^^ + +https://salsa.debian.org/apt-team/python-apt.git + + This is the official Debian repository of python-apt. + You can clone it using git by doing:: + + git clone git://salsa.debian.org/apt-team/python-apt.git + + + All code which will be uploaded to Debian is here. + There are also branches for Ubuntu releases, but those may not be up-to-date. + + Branch names consist of the distribution vendor, followed by a slash, + followed by the release of that distribution, for example: ``debian/sid``. + + The current working branch is usually pointed to by ``HEAD``, it is + either ``debian/sid`` or ``debian/experimental``. + + If both sid and experimental are active, bug fixes are either cherry-picked from + ``debian/experimental`` to ``debian/sid``, or a new release is cut on the sid branch + and then merged into experimental. + + Updates to stable release branches, such as ``debian/wheezy``, are almost always + cherry-picked or backported from the ``debian/sid`` branch. + + +.. highlight:: cpp + +C++ Coding style +---------------- +This document gives coding conventions for the C++ code comprising +the C++ extensions of Python APT. Please see the companion +informational PEP describing style guidelines for Python code (:PEP:`8`). + +Note, rules are there to be broken. Two good reasons to break a +particular rule: + + (1) When applying the rule would make the code less readable, even + for someone who is used to reading code that follows the rules. + + (2) To be consistent with surrounding code that also breaks it + (maybe for historic reasons) -- although this is also an + opportunity to clean up someone else's mess (in true XP style). + +This part of the document is derived from :PEP:`7` which was written by +Guido van Rossum. + + +C++ dialect +^^^^^^^^^^^ + +- Use ISO standard C++ (the 2011 version of the standard), headers + should also adhere to the 1998 version of the standard. + +- Use C++ style // one-line comments for single-line comments. + +- No compiler warnings with ``gcc -std=c++11 -Wall -Wno-write-strings``. There + should also be no errors with ``-pedantic`` added. + + +Code lay-out +^^^^^^^^^^^^ + +- Use 3-space indents, in files that already use them. In new source files, + that were created after this rule was introduced, use 4-space indents. + + At some point, the whole codebase may be converted to use only + 4-space indents. + +- No line should be longer than 79 characters. If this and the + previous rule together don't give you enough room to code, your + code is too complicated -- consider using subroutines. + +- No line should end in whitespace. If you think you need + significant trailing whitespace, think again -- somebody's + editor might delete it as a matter of routine. + +- Function definition style: function name in column 2, outermost + curly braces in column 1, blank line after local variable + declarations:: + + static int extra_ivars(PyTypeObject *type, PyTypeObject *base) + { + int t_size = PyType_BASICSIZE(type); + int b_size = PyType_BASICSIZE(base); + + assert(t_size >= b_size); /* type smaller than base! */ + ... + return 1; + } + +- Code structure: one space between keywords like 'if', 'for' and + the following left paren; no spaces inside the paren; braces as + shown:: + + if (mro != NULL) { + ... + } + else { + ... + } + +- The return statement should *not* get redundant parentheses:: + + return Py_None; /* correct */ + return(Py_None); /* incorrect */ + +- Function and macro call style: ``foo(a, b, c)`` -- no space before + the open paren, no spaces inside the parens, no spaces before + commas, one space after each comma. + +- Always put spaces around assignment, Boolean and comparison + operators. In expressions using a lot of operators, add spaces + around the outermost (lowest-priority) operators. + +- Breaking long lines: if you can, break after commas in the + outermost argument list. Always indent continuation lines + appropriately, e.g.:: + + PyErr_Format(PyExc_TypeError, + "cannot create '%.100s' instances", + type->tp_name); + +- When you break a long expression at a binary operator, the + operator goes at the end of the previous line, e.g.:: + + if (type->tp_dictoffset != 0 && base->tp_dictoffset == 0 && + type->tp_dictoffset == b_size && + (size_t)t_size == b_size + sizeof(PyObject *)) + return 0; /* "Forgive" adding a __dict__ only */ + +- Put blank lines around functions, structure definitions, and + major sections inside functions. + +- Comments go before the code they describe. + +- All functions and global variables should be declared static + unless they are to be part of a published interface + + +Naming conventions +^^^^^^^^^^^^^^^^^^ + +- Use a ``Py`` prefix for public functions; never for static + functions. The ``Py_`` prefix is reserved for global service + routines like ``Py_FatalError``; specific groups of routines + (e.g. specific object type APIs) use a longer prefix, + e.g. ``PyString_`` for string functions. + +- Public functions and variables use MixedCase with underscores, + like this: ``PyObject_GetAttr``, ``Py_BuildValue``, ``PyExc_TypeError``. + +- Internal functions and variables use lowercase with underscores, like + this: ``hashes_get_sha1.`` + +- Occasionally an "internal" function has to be visible to the + loader; we use the _Py prefix for this, e.g.: ``_PyObject_Dump``. + +- Macros should have a MixedCase prefix and then use upper case, + for example: ``PyString_AS_STRING``, ``Py_PRINT_RAW``. + + +Documentation Strings +^^^^^^^^^^^^^^^^^^^^^ +- The first line of each function docstring should be a "signature + line" that gives a brief synopsis of the arguments and return + value. For example:: + + PyDoc_STRVAR(myfunction__doc__, + "myfunction(name: str, value) -> bool\n\n" + "Determine whether name and value make a valid pair."); + + The signature line should be formatted using the format for function + annotations described in :PEP:`3107`, whereas the annotations shall reflect + the name of the type (e.g. ``str``). The leading ``def`` and the trailing + ``:`` as used for function definitions must not be included. + + Always include a blank line between the signature line and the + text of the description. + + If the return value for the function is always ``None`` (because + there is no meaningful return value), do not include the + indication of the return type. + +- When writing multi-line docstrings, be sure to always use + string literal concatenation:: + + PyDoc_STRVAR(myfunction__doc__, + "myfunction(name, value) -> bool\n\n" + "Determine whether name and value make a valid pair."); + + +Python Coding Style +------------------- +The coding style for all code written in python is :PEP:`8`. Exceptions from +this rule are the documentation, where code is sometimes formatted differently +to explain aspects. + +When writing code, use tools like pylint, pyflakes, pychecker and pycodestyle +(all available from Debian/Ubuntu) to verify that your code is +OK. Fix all the problems which seem reasonable, and mention the unfixed issues +when asking for merge. + +All code must work on both Python 2 and Python 3. + +Submitting your patch +--------------------- +First of all, the patch you create should be based against the most current +branch of python-apt (debian/sid or debian/experimental). If it is a bugfix, +you should probably use debian/sid. If you choose the wrong branch, we will +ask you to rebase your patches against the correct one. + +Once you have made your change, check that it: + + * conforms to :PEP:`8` (checked with pycodestyle). It should, at least not + introduce new errors. (and never have whitespace at end of line) + * produces no new errors in pychecker, pyflakes and pylint (unless you + can't fix them, but please tell so when requesting the merge, so it can + be fixed before hitting one of the main branches). + * does not change the behaviour of existing code in a non-compatible way. + * works on both Python 2 and Python 3. + +If your change follows all points of the checklist, you can commit it to your +repository. (You could commit it first, and check later, and then commit the +fixes, but commits should be logical and it makes no sense to have to commits +for one logical unit). + +The changelog message should follow standard git format. At the end of the +message, tags understood by gbp-dch and other tags may be added. An example +commit message could be: + +.. code-block:: none + + apt.package: Fix blah blah + + Fix a small bug where foo is doing bar, but should be doing baz + instead. + + Closes: #bugnumber + LP: #ubuntu-bug-number + Reported-By: Bug Reporter Name <email@example.com> + + +Once you have made all your changes, you can run ``git format-patch``, +specifying the upstream commit or branch you want to create patches +against. Then you can either: + +* report a bug against the python-apt package, attach the patches + you created in the previous step, and tag it with 'patch'. It might also be + a good idea to prefix the bug report with '[PATCH]'. + +* send the patches via ``git send-email``. + +For larger patch series, you can also publish a git branch on a +public repository and request it to be pulled. + +If you choose that approach, you may want to base your patches against +the latest release, and not against some random commit, for the sake of +preserving a sane git history. + +Be prepared to rebase such a branch, and close any bugs you fix in the +branch by mentioning them in the commit message using a Closes or LP +tag. + + +Documentation updates +--------------------- +If you want to update the documentation, please follow the procedure as written +above. You can send your content in plain text, but reStructuredText is the +preferred format. I (Julian Andres Klode) will review your patch and include +it. + +.. highlight:: sh + +Example patch session +---------------------- +In the following example, we edit a file, create a patch (an enhanced +patch), and report a wishlist bug with this patch against the python-apt +package:: + + user@ pc:~$ git clone git://anonscm.debian.org/apt/python-apt.git + user@pc:~$ cd python-apt + user@pc:~/python-apt$ editor FILES + user@pc:~/python-apt$ pycodestyle FILES # Check with pycodestyle + user@pc:~/python-apt$ pylint -e FILES # Check with pylint + user@pc:~/python-apt$ pyflakes FILES # Check with pyflakes + user@pc:~/python-apt$ pychecker FILES # Check with pychecker + user@pc:~/python-apt$ git commit -p + user@pc:~/python-apt$ git format-patch origin/HEAD + user@pc:~/python-apt$ reportbug --severity=wishlist --tag=patch --attach=<patch> ... python-apt + +You may also send the patches to the mailing list instead of +reporting the bug:: + + user@pc:~/python-apt$ git send-email --to=deity@lists.debian.org <patches created by format-patch> + +You can even push your changes to your own repository and request +a pull request. diff --git a/doc/source/tutorials/index.rst b/doc/source/tutorials/index.rst new file mode 100644 index 0000000..06d31c6 --- /dev/null +++ b/doc/source/tutorials/index.rst @@ -0,0 +1,8 @@ +Tutorials +========= + +.. toctree:: + :maxdepth: 1 + :glob: + + * |