From: Jiri Konecny jkonecny@redhat.com
--- pyanaconda/bootloader.py | 30 +++-- pyanaconda/flags.py | 2 +- pyanaconda/geoloc.py | 227 ++++++++++++++++++++--------------- pyanaconda/ihelp.py | 12 +- pyanaconda/iutil.py | 8 +- pyanaconda/kickstart.py | 2 +- pyanaconda/nm.py | 72 ++++++----- pyanaconda/ui/common.py | 4 +- pyanaconda/ui/gui/utils.py | 5 +- pyanaconda/ui/tui/__init__.py | 2 +- pyanaconda/ui/tui/hubs/__init__.py | 12 +- pyanaconda/ui/tui/spokes/__init__.py | 12 +- 12 files changed, 217 insertions(+), 171 deletions(-)
diff --git a/pyanaconda/bootloader.py b/pyanaconda/bootloader.py index 0f62d73..2bbce13 100644 --- a/pyanaconda/bootloader.py +++ b/pyanaconda/bootloader.py @@ -56,18 +56,24 @@ def __init__(self): self.flow = None
def parse_serial_opt(arg): - """Parse and split serial console options. - - Documentation/kernel-parameters.txt says: - ttyS<n>[,options] - Use the specified serial port. The options are of - the form "bbbbpnf", where "bbbb" is the baud rate, - "p" is parity ("n", "o", or "e"), "n" is number of - bits, and "f" is flow control ("r" for RTS or - omit it). Default is "9600n8". - but note that everything after the baud rate is optional, so these are - all valid: 9600, 19200n, 38400n8, 9600e7r. - Also note that the kernel assumes 1 stop bit; this can't be changed. + """ + Parse and split serial console options. + + .. NOTE:: + + Documentation/kernel-parameters.txt says: + + ttyS<n>[,options] + + Use the specified serial port. The options are of + the form "bbbbpnf", where "bbbb" is the baud rate, + "p" is parity ("n", "o", or "e"), "n" is number of + bits, and "f" is flow control ("r" for RTS or + omit it). Default is "9600n8". + + but note that everything after the baud rate is optional, so these are + all valid: 9600, 19200n, 38400n8, 9600e7r. + Also note that the kernel assumes 1 stop bit; this can't be changed. """ opts = serial_opts() m = re.match(r'\d+', arg) diff --git a/pyanaconda/flags.py b/pyanaconda/flags.py index ad3aaca..c6c451f 100644 --- a/pyanaconda/flags.py +++ b/pyanaconda/flags.py @@ -122,7 +122,7 @@ def read(self, filenames): Read and parse a filename (or a list of filenames). Files that can't be read are silently ignored. Returns a list of successfully read files. - filenames can contain *, ?, and character ranges expressed with [] + filenames can contain *, ?, and character ranges expressed with [] """
readfiles = [] diff --git a/pyanaconda/geoloc.py b/pyanaconda/geoloc.py index 5daf606..b53e39f 100644 --- a/pyanaconda/geoloc.py +++ b/pyanaconda/geoloc.py @@ -22,81 +22,86 @@ A GeoIP and WiFi location module - location detection based on IP address
How to use the geolocation module - -First call init_geolocation() - this creates the LocationInfo singleton and -you can also use it to set what geolocation provider should be used. -To actually look up current position, call refresh() - this will trigger -the actual online geolocation query, which runs in a thread. -After the look-up thread finishes, the results are stored in the singleton -and can be retrieved using the get_territory_code() and get_result() methods. -If you call these methods without calling refresh() first or if the look-up -is currently in progress, both return None. - + First call :func:`init_geolocation` - this creates the LocationInfo singleton and + you can also use it to set what geolocation provider should be used. + To actually look up current position, call :func:`refresh` - this will trigger + the actual online geolocation query, which runs in a thread. + After the look-up thread finishes, the results are stored in the singleton + and can be retrieved using the :func:`get_territory_code` and :func:`get_result` methods. + If you call these methods without calling :func:`refresh` first or if the look-up + is currently in progress, both return ``None``. + +==================== Geolocation backends +====================
This module currently supports three geolocation backends: -* Fedora GeoIP API -* Hostip GeoIP -* Google WiFi + * Fedora GeoIP API + * Hostip GeoIP + * Google WiFi
Fedora GeoIP backend -This is the default backend. It queries the Fedora GeoIP API for location -data based on current public IP address. The reply is JSON formated and -contains the following fields: -postal_code, latitude, longitude, region, city, country_code, country_name, -time_zone, country_code3, area_code, metro_code, region_name and dma_code -Anaconda currently uses just time_zone and country_code. + This is the default backend. It queries the Fedora GeoIP API for location + data based on current public IP address. The reply is JSON formated and + contains the following fields: + postal_code, latitude, longitude, region, city, country_code, country_name, + time_zone, country_code3, area_code, metro_code, region_name and dma_code + Anaconda currently uses just time_zone and country_code.
Hostip backend -A GeoIP look-up backend that can be used to determine current country code -from current public IP address. The public IP address is determined -automatically when calling the API. -GeoIP results from Hostip contain the current public IP and an approximate -address. To get this detail location info, use the get_result() method -to get an instance of the LocationResult class, used to wrap the result. + A GeoIP look-up backend that can be used to determine current country code + from current public IP address. The public IP address is determined + automatically when calling the API. + GeoIP results from Hostip contain the current public IP and an approximate + address. To get this detail location info, use the get_result() method + to get an instance of the LocationResult class, used to wrap the result.
Google WiFi backend -This backend is probably the most accurate one, at least as long as the -computer has a working WiFi hardware and there are some WiFi APs nearby. -It sends data about nearby APs (ssid, MAC address & signal strength) -acquired from Network Manager to a Google API to get approximate -geographic coordinates. If there are enough AP nearby (such as in a -normal city) it can be very accurate, even up to currently determining -which building is the computer currently in. -But this only returns current geographic coordinates, to get country code -the Nominatim reverse-geocoding API is called to convert the coordinates -to an address, which includes a country code. + This backend is probably the most accurate one, at least as long as the + computer has a working WiFi hardware and there are some WiFi APs nearby. + It sends data about nearby APs (ssid, MAC address & signal strength) + acquired from Network Manager to a Google API to get approximate + geographic coordinates. If there are enough AP nearby (such as in a + normal city) it can be very accurate, even up to currently determining + which building is the computer currently in. + But this only returns current geographic coordinates, to get country code + the Nominatim reverse-geocoding API is called to convert the coordinates + to an address, which includes a country code. + While having many advantages, this backend also has some severe disadvantages: -* needs working WiFi hardware -* tells your public IP address & possibly quite precise geographic coordinates - to two external entities (Google and Nominatim) + * needs working WiFi hardware + * tells your public IP address & possibly quite precise geographic coordinates + to two external entities (Google and Nominatim) + This could have severe privacy issues and should be carefully considered before -enabling it to be used by default. -* the Google WiFi geolocation API seems to lack official documentation +enabling it to be used by default. Also the Google WiFi geolocation API seems to +lack official documentation. + As a result its long-term stability might not be guarantied.
Possible issues with GeoIP - -"I'm in Switzerland connected to corporate VPN and anaconda tells me -I'm in Netherlands." -The public IP address is not directly mapped to the physical location -of a computer. So while your world visible IP address is registered to -an IP block assigned to an ISP in Netherlands, it is just the external -address of the Internet gateway of your corporate network. -As VPNs and proxies can connect two computers anywhere on Earth, -this issue is unfortunately probably unsolvable. + "I'm in Switzerland connected to corporate VPN and anaconda tells me + I'm in Netherlands." + The public IP address is not directly mapped to the physical location + of a computer. So while your world visible IP address is registered to + an IP block assigned to an ISP in Netherlands, it is just the external + address of the Internet gateway of your corporate network. + As VPNs and proxies can connect two computers anywhere on Earth, + this issue is unfortunately probably unsolvable.
Backends that could possibly be used in the future -* GPS geolocation -+ doesn't leak your coordinates to a third party -(not entirely true for assisted GPS) -- unassisted cold GPS startup can take tens of minutes to acquire a GPS fix -+ assisted GPS startup (as used in most smartphones) can acquire a fix -in a couple seconds -* cell tower geolocation + * GPS geolocation + + * (+) doesn't leak your coordinates to a third party + (not entirely true for assisted GPS) + * (-) unassisted cold GPS startup can take tens of minutes to acquire a GPS fix + * (+) assisted GPS startup (as used in most smartphones) can acquire a fix + in a couple seconds + + * cell tower geolocation
""" from pyanaconda.iutil import requests_session @@ -146,20 +151,25 @@ def refresh():
def get_territory_code(wait=False): - """This function returns the current country code - or None, if: - - no results were found - - the lookup is still in progress - - the geolocation module was not activated (init & refresh were not called) - - this is for example the case during image and directory installs + """ + This function returns the current country code or ``None``, if: + + * no results were found + * the lookup is still in progress + * the geolocation module was not activated + (:func:`init_geolocation` & :func:`refresh` were not called) + + * this is for example the case during image and directory installs
:param wait: wait for lookup in progress to finish - False - don't wait - True - wait for default period - number - wait for up to number seconds + + | ``False`` - don't wait + | ``True`` - wait for default period + | number - wait for up to number seconds + :type wait: bool or number - :return: current country code or None if not known - :rtype: string or None + :return: current country code or ``None`` if not known + :rtype: string or ``None`` """ if _get_location_info_instance(wait): return location_info_instance.get_territory_code() @@ -168,20 +178,25 @@ def get_territory_code(wait=False):
def get_timezone(wait=False): - """This function returns the current time zone - or None, if: - - no timezone was found - - the lookup is still in progress - - the geolocation module was not activated (init & refresh were not called) - - this is for example the case during image and directory installs + """ + This function returns the current time zone or ``None``, if: + + * no timezone was found + * the lookup is still in progress + * the geolocation module was not activated + (:func:`init_geolocation` & :func:`refresh` were not called) + + * this is for example the case during image and directory installs
:param wait: wait for lookup in progress to finish - False - don't wait - True - wait for default period - number - wait for up to number seconds + + | ``False`` - don't wait + | ``True`` - wait for default period + | number - wait for up to number seconds + :type wait: bool or number - :return: current timezone or None if not known - :rtype: string or None + :return: current timezone or ``None`` if not known + :rtype: string or ``None`` """ if _get_location_info_instance(wait): return location_info_instance.get_timezone() @@ -190,20 +205,25 @@ def get_timezone(wait=False):
def get_result(wait=False): - """Returns the current geolocation result wrapper - or None, if: - - no results were found - - the refresh is still in progress - - the geolocation module was not activated (init & refresh were not called) - - this is for example the case during image and directory installs + """ + Returns the current geolocation result wrapper or ``None``, if: + + * no results were found + * the refresh is still in progress + * the geolocation module was not activated + (:func:`init_geolocation` & :func:`refresh` were not called) + + * this is for example the case during image and directory installs
:param wait: wait for lookup in progress to finish - False - don't wait - True - wait for default period - number - wait for up to number seconds + + | ``False`` - don't wait + | ``True`` - wait for default period + | number - wait for up to number seconds + :type wait: bool or number - :return: LocationResult instance or None if location is unknown - :rtype: LocationResult or None + :return: :class:`LocationResult` instance or ``None`` if location is unknown + :rtype: :class:`LocationResult` or ``None`` """ if _get_location_info_instance(wait): return location_info_instance.get_result() @@ -212,7 +232,8 @@ def get_result(wait=False):
def get_provider_id_from_option(option_string): - """Get a valid provider id from a string + """ + Get a valid provider id from a string This function is used to parse command line arguments/boot options for the geolocation module.
@@ -253,7 +274,8 @@ def _get_provider(provider_id):
def _get_location_info_instance(wait=False): - """Return instance of the location info object + """ + Return instance of the location info object and optionally wait for the Geolocation thread to finish
If there is no lookup in progress (no Geolocation refresh thread @@ -304,7 +326,8 @@ class GeolocationError(Exception):
class LocationInfo(object): - """Determines current location based on IP address or + """ + Determines current location based on IP address or nearby WiFi access points (depending on what backend is used) """
@@ -314,7 +337,7 @@ def __init__(self, """ :param provider_id: GeoIP provider id specified by module constant :param refresh_now: if a GeoIP information refresh should be done - once the class is initialized + once the class is initialized :type refresh_now: bool """ self._provider = _get_provider(provider_id) @@ -341,18 +364,22 @@ def refresh(self): " - no connectivity")
def get_result(self): - """Get result from the provider + """ + Get result from the provider + + :return: the result object or return ``None`` if no results are available + :rtype: :class:`LocationResult` or ``None``
- :return: the result object or return None if no results are available - :rtype: LocationResult or None """ return self._provider.get_result()
def get_territory_code(self): - """A convenience function for getting the current territory code + """ + A convenience function for getting the current territory code + + :return: territory code or ``None`` if no results are available + :rtype: string or ``None``
- :return: territory code or None if no results are available - :rtype: string or None """ result = self._provider.get_result() if result: @@ -832,7 +859,7 @@ def scan(self): def get_results(self): """ :return: a list of WiFiAccessPoint objects or - an empty list if no APs were found or the scan failed + an empty list if no APs were found or the scan failed """ return self._scan_results
diff --git a/pyanaconda/ihelp.py b/pyanaconda/ihelp.py index 470f300..d437ab6 100644 --- a/pyanaconda/ihelp.py +++ b/pyanaconda/ihelp.py @@ -33,12 +33,13 @@ yelp_process = None
def _get_best_help_file(help_folder, help_file): - """Return the path to the best help file for the current language and available + """ + Return the path to the best help file for the current language and available help content
:param str help_folder: a path to folder where we should look for the help files :param str help_file: name of the requested help file - :return: path to the best help file or None is no match is found + :return: path to the best help file or ``None`` is no match is found :rtype: str or NoneType
""" @@ -73,9 +74,11 @@ def _get_best_help_file(help_folder, help_file): return None
def get_help_path(help_file, instclass): - """Return the full path for the given help file name, + """ + Return the full path for the given help file name, if the help file path does not exist a fallback path is returned. There are actually two possible fallback paths that might be returned: + * first we try to return path to the main page of the installation guide (if it exists) * if we can't find the main page of the installation page, path to a @@ -112,7 +115,8 @@ def get_help_path(help_file, instclass): return _get_best_help_file(instclass.help_folder, instclass.help_placeholder)
def start_yelp(help_path): - """Start a new yelp process and make sure to kill any existing ones + """ + Start a new yelp process and make sure to kill any existing ones
:param help_path: path to the help file yelp should load :type help_path: str or NoneType diff --git a/pyanaconda/iutil.py b/pyanaconda/iutil.py index 07fc4cb..63c7b42 100644 --- a/pyanaconda/iutil.py +++ b/pyanaconda/iutil.py @@ -1067,7 +1067,7 @@ def conditional_chown(path, uid, gid, from_uid=None, from_gid=None): def is_unsupported_hw(): """ Check to see if the hardware is supported or not.
- :returns: True if this is unsupported hardware, False otherwise + :returns: ``True`` if this is unsupported hardware, ``False`` otherwise :rtype: bool """ try: @@ -1082,14 +1082,14 @@ def is_unsupported_hw():
def ensure_str(str_or_bytes, keep_none=True): """ - Returns a str instance for given string or None if requested to keep it. + Returns a str instance for given string or ``None`` if requested to keep it.
:param str_or_bytes: string to be kept or converted to str type :type str_or_bytes: str or bytes :param bool keep_none: whether to keep None as it is or raise ValueError if - None is passed + ``None`` is passed :raises ValueError: if applied on an object not being of type bytes nor str - (nor NoneType if :param:`keep_none` is False) + (nor NoneType if ``keep_none`` is ``False``) """
if keep_none and str_or_bytes is None: diff --git a/pyanaconda/kickstart.py b/pyanaconda/kickstart.py index f131e5a..52e8ef7 100644 --- a/pyanaconda/kickstart.py +++ b/pyanaconda/kickstart.py @@ -88,7 +88,7 @@ class AnacondaKSScript(KSScript): This will write the script to a file named /tmp/ks-script- before execution. Output is logged by the program logger, the path specified by --log - or to /tmp/ks-script-*.log + or to /tmp/ks-script-*.log """ def run(self, chroot): """ Run the kickstart script diff --git a/pyanaconda/nm.py b/pyanaconda/nm.py index de21b46..02b9a11 100644 --- a/pyanaconda/nm.py +++ b/pyanaconda/nm.py @@ -474,7 +474,7 @@ def nm_device_active_ssid(name):
:param name: name of device :type name: str - :return ssid of active access point, None if device has no active AP + :return: ssid of active access point, ``None`` if device has no active AP :rtype: str :raise UnknownDeviceError: if device is not found """ @@ -501,10 +501,12 @@ def nm_device_ip_config(name, version=4): :type version: int :return: IP configuration of device, empty list if device is not in ACTIVATED state - :rtype: [[[address1, prefix1, gateway1], [address2, prefix2, gateway2], ...], - [nameserver1, nameserver2]] - addressX, gatewayX: string - prefixX: int + :rtype: + | [[[address1, prefix1, gateway1], [address2, prefix2, gateway2], ...], + | [nameserver1, nameserver2]] + | addressX, gatewayX: string + | prefixX: int + :raise UnknownDeviceError: if device is not found :raise PropertyNotFoundError: if ip configuration is not found """ @@ -857,17 +859,19 @@ def nm_activate_device_connection(dev_name, con_uuid): def nm_add_connection(values): """Add new connection specified by values.
- :param values: list of settings with new values and its types - [[key1, key2, value, type_str], ...] - key1: first-level key of setting (eg "connection") - key2: second-level key of setting (eg "uuid") - value: new value - type_str: dbus type of new value (eg "ay") - :type values: [[key1, key2, value, type_str], ...] - key1: str - key2: str - value: object - type_str: str + :param values: + | list of settings with new values and its types + | [[key1, key2, value, type_str], ...] + | key1: first-level key of setting (eg "connection") + | key2: second-level key of setting (eg "uuid") + | value: new value + | type_str: dbus type of new value (eg "ay") + :type values: + | [[key1, key2, value, type_str], ...] + | key1: str + | key2: str + | value: object + | type_str: str """
settings = {} @@ -907,27 +911,31 @@ def nm_update_settings_of_device(name, new_values):
The type of value is determined from existing settings of device. If setting for key1, key2 does not exist, default_type_str is used or - if None, the type is inferred from the value supplied (string and bool only). + if ``None``, the type is inferred from the value supplied (string and bool only).
:param name: name of device :type name: str - :param new_values: list of settings with new values and its types - [[key1, key2, value, default_type_str]] - key1: first-level key of setting (eg "connection") - key2: second-level key of setting (eg "uuid") - value: new value - default_type_str: dbus type of new value to be used - if the setting does not already exist; - if None, the type is inferred from - value (string and bool only) - :type new_values: [[key1, key2, value, default_type_str], ...] - key1: str - key2: str - value: - default_type_str: str + :param new_values: + | list of settings with new values and its types + | [[key1, key2, value, default_type_str]] + | key1: first-level key of setting (eg "connection") + | key2: second-level key of setting (eg "uuid") + | value: new value + | default_type_str: + + dbus type of new value to be used + if the setting does not already exist; + if ``None``, the type is inferred from + value (string and bool only) + :type new_values: + | [[key1, key2, value, default_type_str], ...] + | key1: str + | key2: str + | value: + | default_type_str: str :raise UnknownDeviceError: if device is not found :raise SettingsNotFoundError: if settings were not found - (eg for "wlan0") + (eg for "wlan0") """ settings_paths = _device_settings(name) if not settings_paths: diff --git a/pyanaconda/ui/common.py b/pyanaconda/ui/common.py index 5c128d5..78f8fd0 100644 --- a/pyanaconda/ui/common.py +++ b/pyanaconda/ui/common.py @@ -709,10 +709,10 @@ def collect_categories(mask_paths, displaymode): return categories
def collectCategoriesAndSpokes(paths, klass, displaymode): - """collects categories and spokes to be displayed on this Hub + """Collects categories and spokes to be displayed on this Hub
:param paths: dictionary mapping categories, spokes, and hubs to their - their respective search path(s) + their respective search path(s) :return: dictionary mapping category class to list of spoke classes :rtype: dictionary[category class] -> [ list of spoke classes ] """ diff --git a/pyanaconda/ui/gui/utils.py b/pyanaconda/ui/gui/utils.py index 676416f..05737a9 100644 --- a/pyanaconda/ui/gui/utils.py +++ b/pyanaconda/ui/gui/utils.py @@ -146,11 +146,12 @@ def gtk_batch_map(action, items, args=(), pre_func=None, batch_size=1): time. If a pre-processing function is given it is mapped on the items first before the action happens in the main thread.
- MUST NOT BE CALLED NOR WAITED FOR FROM THE MAIN THREAD. + .. DANGER:: + MUST NOT BE CALLED NOR WAITED FOR FROM THE MAIN THREAD.
:param action: any action that has to be done on the items in the main thread - :type action: (action_item, *args) -> None + :type action: (action_item, *args) -> None :param items: an iterable of items that the action should be mapped on :type items: iterable :param args: additional arguments passed to the action function diff --git a/pyanaconda/ui/tui/__init__.py b/pyanaconda/ui/tui/__init__.py index 18feafb..bb435c9 100644 --- a/pyanaconda/ui/tui/__init__.py +++ b/pyanaconda/ui/tui/__init__.py @@ -56,7 +56,7 @@ def exception_msg_handler(event, data): class TextUserInterface(ui.UserInterface): """This is the main class for Text user interface.
- .. inheritance-diagram:: SourceSpoke + .. inheritance-diagram:: TextUserInterface :parts: 3 """
diff --git a/pyanaconda/ui/tui/hubs/__init__.py b/pyanaconda/ui/tui/hubs/__init__.py index 1736876..24eb385 100644 --- a/pyanaconda/ui/tui/hubs/__init__.py +++ b/pyanaconda/ui/tui/hubs/__init__.py @@ -26,14 +26,14 @@
class TUIHub(TUIObject, common.Hub): """Base Hub class implementing the pyanaconda.ui.common.Hub interface. - It uses text based categories to look for relevant Spokes and manages - all the spokes it finds to have the proper category. + It uses text based categories to look for relevant Spokes and manages + all the spokes it finds to have the proper category.
- :param categories: list all the spoke categories to be displayed in this Hub - :type categories: list of strings + :param categories: list all the spoke categories to be displayed in this Hub + :type categories: list of strings
- :param title: title for this Hub - :type title: str + :param title: title for this Hub + :type title: str
.. inheritance-diagram:: TUIHub :parts: 3 diff --git a/pyanaconda/ui/tui/spokes/__init__.py b/pyanaconda/ui/tui/spokes/__init__.py index fbf9366..06445a3 100644 --- a/pyanaconda/ui/tui/spokes/__init__.py +++ b/pyanaconda/ui/tui/spokes/__init__.py @@ -36,14 +36,14 @@ # pylint: disable=abstract-method class TUISpoke(TUIObject, tui.Widget, Spoke): """Base TUI Spoke class implementing the pyanaconda.ui.common.Spoke API. - It also acts as a Widget so we can easily add it to Hub, where is shows - as a summary box with title, description and completed checkbox. + It also acts as a Widget so we can easily add it to Hub, where is shows + as a summary box with title, description and completed checkbox.
- :param title: title of this spoke - :type title: str + :param title: title of this spoke + :type title: str
- :param category: category this spoke belongs to - :type category: string + :param category: category this spoke belongs to + :type category: string
.. inheritance-diagram:: TUISpoke :parts: 3