This page describes Libcloud development process and contains general guidelines and information on how to contribute to the project.
We welcome contributions of any kind (ideas, code, tests, documentation, examples, ...).
If you need help or get stuck at any point during this process, stop by on our IRC channel (:ref:`#libcloud on freenode <irc>`) and we will do our best to assist you.
black tox target (tox -eblack).isort tox target (tox -e
isort).lint,pylint,black - tox
-elint,pylint,black,isort.And most importantly, follow the existing style in the file you are editing and be consistent.
To make complying with our style guide easier, we provide a git pre-commit hook which automatically checks modified Python files for violations of our style guide.
You can install it by running following command in the root of the repository checkout:
ln -s contrib/pre-commit.sh .git/hooks/pre-commit
After you have installed this hook it will automatically check modified Python files for violations before a commit. If a violation is found, commit will be aborted.
This section describes some general code conventions you should follow when writing a Libcloud code.
Organize the imports in the following order:
Each section should be separated with a blank line. For example:
import sys
import base64
import paramiko
from libcloud.compute.base import Node, NodeDriver
from libcloud.compute.providers import Provider
Functions in a module and methods on a class should be organized in the following order:
For example:
class Unicorn(object):
def __init__(self, name='fluffy'):
self._name = name
def make_a_rainbow(self):
pass
def _get_rainbow_colors(self):
pass
def __eq__(self, other):
return self.name == other.name
Methods on a driver class should be organized in the following order:
Methods which perform a similar functionality should be grouped together and defined one after another.
For example:
class MyDriver(object):
def __init__(self):
pass
def list_nodes(self):
pass
def list_images(self):
pass
def create_node(self):
pass
def reboot_node(self):
pass
def ex_create_image(self):
pass
def _to_nodes(self):
pass
def _to_node(self):
pass
def _to_images(self):
pass
def _to_image(self):
pass
Methods should be ordered this way for the consistency reasons and to make reading and following the generated API documentation easier.
For better readability and understanding of the code, prefer keyword over regular arguments.
Good:
some_method(public_ips=public_ips, private_ips=private_ips)
Bad:
some_method(public_ips, private_ips)
You should always explicitly declare arguments in a function or a method
signature and only use **kwargs and *args respectively when there is a
valid use case for it.
Using **kwargs in many contexts is against Python's "explicit is better
than implicit" mantra and makes it for a bad and a confusing API. On top of
that, it makes many useful things such as programmatic API introspection hard
or impossible.
A use case when it might be valid to use **kwargs is a decorator.
Good:
def my_method(self, name, description=None, public_ips=None):
pass
Bad (please avoid):
def my_method(self, name, **kwargs):
description = kwargs.get('description', None)
public_ips = kwargs.get('public_ips', None)
Dynamic nature of Python can be very nice and useful, but if (ab)use it in a wrong way it can also make it hard for the API consumer to understand what is going on and what kind of values are being returned.
If you have a function or a method which returns a dictionary, make sure to explicitly document in the docstring which keys the returned dictionary contains.
When checking if a variable is provided or defined, prefer to use
if foo is not None instead of if foo.
If you use if foo approach, it's easy to make a mistake when a valid value
can also be falsy (e.g. a number 0).
For example:
class SomeClass(object):
def some_method(self, domain=None):
params = {}
if domain is not None:
params['Domain'] = domain
For documenting the API we we use Sphinx and reStructuredText syntax. Docstring conventions to which you should adhere to are described below.
*args and
**kwargs.:param p: or :keyword p:
and :type p: annotation.:param p: ... - A description of the parameter p for a function
or method.:keyword p: ... - A description of the keyword parameter p.:type p: ... The expected type of the parameter p.:return: and :rtype
annotation.:return: ... A description of return value for a function or method.:rtype: ... The type of the return value for a function or method.(required) notation in
description. For example: :keyword image: OS Image to boot on node. (required)
or
For example: :type auth: :class:`.NodeAuthSSHKey` or :class:`.NodeAuthPassword`
<container_type> of <objects_type>. For example:
:rtype: `list` of :class:`Node`
For more information and examples, please refer to the following links:
Node sizing data for most providers is stored in-line as a module level constant in the corresponding provide module.
An exception to that is AWS EC2 which sizing data is automatically generated and scraped from AWS API as documented below.
To update EC2 sizing data, you just need to run scrape-ec2-sizes tox target
and commit the changed files
(libcloud/compute/constants/ec2_instance_types.py,
libcloud/compute/constants/ec2_region_details_complete.py).
To add a new region update contrib/scrape-ec2-prices.py and
contrib/scrape-ec2-sizes.py file (example
https://github.com/apache/libcloud/commit/762f0e5623b6f9837204ffe27d825b236c9c9970)
and then re-run corresponding tox targets as shown below:
tox -escrape-ec2-sizes,scrape-ec2-prices
Pricing data for some provides is automatically scraped using
scrape-and-publish-provider-prices tox target (this target required valid
AWS and Google Cloud API keys to be set for it to work).
This tox target is ran before making a new release which means that each release includes pricing data which has been updated on the day of the release.
In addition to that, that tox target runs daily as part of our CI/CD system and the latest version of that file is published to a public read-only S3 bucket.
For more information on how to utilize that pricing data, please see :doc:`Pricing </compute/pricing>` page.
If you are implementing a big feature or a change, start a discussion on the :ref:`issue tracker <issue-tracker>` or the :ref:`mailing list <mailing-lists>` first.
Go to our issue tracker and open a new issue for your changes there. This issue will be used as an umbrella place for your changes. As such, it will be used to track progress and discuss implementation details.
Fork our Github git repository. Your fork will be used to hold your changes.
For example:
git checkout -b <change_name>
Make sure that all the code you have added or modified has appropriate test coverage. Also make sure all the tests including the existing ones still pass.
Use libcloud.test.unittest as the unit testing package to ensure that
your tests work with older versions of Python.
For more information on how to write and run tests, please see :doc:`Testing page </testing>`.
Commit your changes.
For example:
git commit -m "Add a new compute driver for CloudStack based providers."
Go to https://github.com/apache/libcloud/ and open a new pull request with your changes. Your pull request will appear at https://github.com/apache/libcloud/pulls.
Wait for your changes to be reviewed and address any outstanding comments.
If you are contributing a bigger change (e.g. large new feature or a new provider driver) you need to have signed Apache Individual Contributor License Agreement (ICLA) in order to have your patch accepted.
You can find more information on how to sign and file an ICLA on the Apache website.
When filling the form, leave field preferred Apache id(s) empty and in
the notify project field, enter Libcloud.
Libcloud supports a variety of Python versions so your code also needs to work with all the supported versions. This means that in some cases you will need to include extra code to make sure it works in all the supported versions.
Some examples which show how to handle those cases are described below.
Context managers aren't available in Python 2.5 by default. If you want to use
them make sure to put from __future__ import with_statement on top of the
file where you use them.
You can find a lot of utility functions which make code easier to work with
Python 2.x and 3.x in libcloud.utils.py3 module.
You can find some more information on changes which are involved in making the code work with multiple versions on the following link - Lessons learned while porting Libcloud to Python 3
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。