Skip to content

Commit

Permalink
refactor: improve typing and delete mutable default arguments (#422)
Browse files Browse the repository at this point in the history
  • Loading branch information
12rambau authored Jan 22, 2025
2 parents 50d0a48 + 1aa998c commit 7c9c733
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 126 deletions.
29 changes: 15 additions & 14 deletions geetools/ee_authenticate.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Toolbox for the :py:func:`ee.Authenticate` function."""
from __future__ import annotations

import os
from contextlib import suppress
from pathlib import Path
from shutil import move
Expand All @@ -16,14 +17,14 @@ class AuthenticateAccessor:
"""Create an accessor for the :py:func:`ee.Authenticate` function."""

@staticmethod
def new_user(name: str = "", credential_pathname: str = "") -> None:
def new_user(name: str = "", credential_pathname: str | os.PathLike = ""):
"""Authenticate the user and save the credentials in a specific folder.
Equivalent to :py:func:`ee.Authenticate` but where the registered user will not be the default one (the one you get when running :py:func:`ee.Initialize`)
Equivalent to :py:func:`ee.Authenticate` but where the registered user will not be the default one (the one you get when running :py:func:`ee.Initialize`).
Args:
name: The name of the user. If not set, it will reauthenticate default.
credential_pathname: The path to the folder where the credentials are stored. If not set, it uses the default path
credential_pathname: The path to the folder where the credentials are stored. If not set, it uses the default path.
Example:
.. code-block:: python
Expand Down Expand Up @@ -55,12 +56,12 @@ def new_user(name: str = "", credential_pathname: str = "") -> None:
move(Path(dir) / default.name, default)

@staticmethod
def delete_user(name: str = "", credential_pathname: str = "") -> None:
def delete_user(name: str = "", credential_pathname: str | os.PathLike = ""):
"""Delete a user credential file.
Args:
name: The name of the user. If not set, it will delete the default user
credential_pathname: The path to the folder where the credentials are stored. If not set, it uses the default path
name: The name of the user. If not set, it will delete the default user.
credential_pathname: The path to the folder where the credentials are stored. If not set, it uses the default path.
Example:
.. code-block:: python
Expand All @@ -80,16 +81,16 @@ def delete_user(name: str = "", credential_pathname: str = "") -> None:
(credential_path / name).unlink()

@staticmethod
def list_user(credential_pathname: str = "") -> list[str]:
def list_user(credential_pathname: str | os.PathLike = "") -> list[str]:
"""return all the available users in the set folder.
To reach "default" simply omit the ``name`` parameter in the User methods
To reach "default" simply omit the ``name`` parameter in the User methods.
Args:
credential_pathname: The path to the folder where the credentials are stored. If not set, it uses the default path
credential_pathname: The path to the folder where the credentials are stored. If not set, it uses the default path.
Returns:
A list of strings with the names of the users
A list of strings with the names of the users.
Example:
.. code-block:: python
Expand All @@ -105,13 +106,13 @@ def list_user(credential_pathname: str = "") -> list[str]:
return [f.name.replace("credentials", "") or "default" for f in files]

@staticmethod
def rename_user(new: str, old: str = "", credential_pathname: str = "") -> None:
def rename_user(new: str, old: str = "", credential_pathname: str | os.PathLike = ""):
"""Rename a user without changing the credentials.
Args:
new: The new name of the user
old: The name of the user to rename
credential_pathname: The path to the folder where the credentials are stored. If not set, it uses the default path
new: The new name of the user.
old: The name of the user to rename.
credential_pathname: The path to the folder where the credentials are stored. If not set, it uses the default path.
Example:
.. code-block:: python
Expand Down
8 changes: 4 additions & 4 deletions geetools/ee_feature.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def toFeatureCollection(self) -> ee.FeatureCollection:
"""Convert a :py:class:`ee.Feature` composed of a multiGeometry geometry into a :py:class:`ee.FeatureCollection`.
Returns:
The :py:class:`ee.FeatureCollection`
The :py:class:`ee.FeatureCollection`.
Example:
.. jupyter-execute::
Expand All @@ -37,14 +37,14 @@ def toFeatureCollection(self) -> ee.FeatureCollection:
fc = geoms.map(lambda g: self._obj.setGeometry(g))
return ee.FeatureCollection(fc)

def removeProperties(self, properties: list | ee.List) -> ee.Feature:
def removeProperties(self, properties: list[str] | ee.List) -> ee.Feature:
"""Remove properties from a feature.
Args:
properties : List of properties to remove
properties : List of properties to remove.
Returns:
The feature without the properties
The feature without the properties.
Example:
.. jupyter-execute::
Expand Down
56 changes: 33 additions & 23 deletions geetools/ee_feature_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ def toImage(
return ee.Image().paint(self._obj, **params)

def toDictionary(
self, keyColumn: str | ee.String = "system:index", selectors: list | ee.List = []
self,
keyColumn: str | ee.String = "system:index",
selectors: list[str] | ee.List | None = None,
) -> ee.Dictionary:
"""Convert to Dictionary.
Expand Down Expand Up @@ -125,7 +127,9 @@ def toDictionary(
print(json.dumps(countries.getInfo(), indent=2))
"""
uniqueIds = self._obj.aggregate_array(keyColumn)
selectors = ee.List(selectors) if selectors else self._obj.first().propertyNames()
selectors = (
ee.List(selectors) if selectors is not None else self._obj.first().propertyNames()
)
keyColumn = ee.String(keyColumn)

features = self._obj.toList(self._obj.size())
Expand Down Expand Up @@ -270,8 +274,8 @@ def removeNonPoly(feat):
def byProperties(
self,
featureId: str | ee.String = "system:index",
properties: list | ee.List = [],
labels: list = [],
properties: list[str] | ee.List | None = None,
labels: list[str] | ee.List | None = None,
) -> ee.Dictionary:
"""Get a dictionary with all feature values for each property.
Expand All @@ -289,7 +293,7 @@ def byProperties(
Args:
featureId: The property used to label features. Defaults to ``"system:index"``.
properties: A list of properties to get the values from.
properties: A list of properties to get the values from. By default, all properties will be used.
labels: A list of names to replace properties names. Default to the properties names.
Returns:
Expand Down Expand Up @@ -319,22 +323,24 @@ def byProperties(
features = features.map(lambda i: ee.Algorithms.If(isString(i), i, ee.Number(i).format()))

# retrieve properties for each feature
properties = ee.List(properties) if properties else self._obj.first().propertyNames()
properties = (
ee.List(properties) if properties is not None else self._obj.first().propertyNames()
)
properties = properties.remove(featureId)
values = properties.map(
lambda p: ee.Dictionary.fromLists(features, self._obj.aggregate_array(p))
)

# get the label to use in the dictionary if requested
labels = ee.List(labels) if labels else properties
labels = ee.List(labels) if labels is not None else properties

return ee.Dictionary.fromLists(labels, values)

def byFeatures(
self,
featureId: str | ee.String = "system:index",
properties: list | ee.List = [],
labels: list = [],
properties: list[str] | ee.List | None = None,
labels: list[str] | ee.List | None = None,
) -> ee.Dictionary:
"""Get a dictionary with all property values for each feature.
Expand All @@ -352,7 +358,7 @@ def byFeatures(
Args:
featureId: The property to use as the feature id. Defaults to ``"system:index"``. This property needs to be a string property.
properties: A list of properties to get the values from.
properties: A list of properties to get the values from. By default, all properties will be used.
labels: A list of names to replace properties names. Default to the properties names.
Returns:
Expand All @@ -378,9 +384,9 @@ def byFeatures(
"""
# compute the properties and their labels
props = ee.List(properties) if properties else self._obj.first().propertyNames()
props = ee.List(properties) if properties is not None else self._obj.first().propertyNames()
props = props.remove(featureId)
labels = ee.List(labels) if labels else props
labels = ee.List(labels) if labels is not None else props

# create a function to get the properties of a feature
# we need to map the featureCollection into a list as it's not possible to return something else than a
Expand All @@ -401,9 +407,9 @@ def plot_by_features(
self,
type: str = "bar",
featureId: str = "system:index",
properties: list = [],
labels: list = [],
colors: list = [],
properties: list[str] | None = None,
labels: list[str] | None = None,
colors: list[str] | None = None,
ax: Axes | None = None,
**kwargs,
) -> Axes:
Expand Down Expand Up @@ -453,14 +459,18 @@ def plot_by_features(
label.set_rotation(45)
"""
# Get the features and properties
props = ee.List(properties) if properties else self._obj.first().propertyNames().getInfo()
props = (
ee.List(properties)
if properties is not None
else self._obj.first().propertyNames().getInfo()
)
props = props.remove(featureId)

# get the data from server
data = self.byProperties(featureId, props, labels).getInfo()

# reorder the data according to the labels or properties set by the user
labels = labels if labels else props.getInfo()
labels = labels if labels is not None else props.getInfo()
data = {k: data[k] for k in labels}

return plot_data(type=type, data=data, label_name=featureId, colors=colors, ax=ax, **kwargs)
Expand All @@ -469,9 +479,9 @@ def plot_by_properties(
self,
type: str = "bar",
featureId: str = "system:index",
properties: list | ee.List = [],
labels: list = [],
colors: list = [],
properties: list[str] | ee.List | None = None,
labels: list[str] | None = None,
colors: list[str] | None = None,
ax: Axes | None = None,
**kwargs,
) -> Axes:
Expand Down Expand Up @@ -516,14 +526,14 @@ def plot_by_properties(
"""
# Get the features and properties
fc = self._obj
props = ee.List(properties) if properties else fc.first().propertyNames()
props = ee.List(properties) if properties is not None else fc.first().propertyNames()
props = props.remove(featureId)

# get the data from server
data = self.byFeatures(featureId, props, labels).getInfo()

# reorder the data according to the lapbes or properties set by the user
labels = labels if labels else props.getInfo()
labels = labels if labels is not None else props.getInfo()
data = {f: {k: data[f][k] for k in labels} for f in data.keys()}

return plot_data(type=type, data=data, label_name=featureId, colors=colors, ax=ax, **kwargs)
Expand All @@ -533,7 +543,7 @@ def plot_hist(
property: str | ee.String,
label: str = "",
ax: Axes | None = None,
color=None,
color: str | None = None,
**kwargs,
) -> Axes:
"""Plot the histogram of a specific property.
Expand Down
Loading

0 comments on commit 7c9c733

Please sign in to comment.