Skip to content

Commit

Permalink
Add new standards for deprecating APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
ndrluis committed Dec 11, 2024
1 parent 7a83695 commit d2ad204
Show file tree
Hide file tree
Showing 16 changed files with 932 additions and 249 deletions.
9 changes: 9 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -233,3 +233,12 @@ Home page: https://hive.apache.org/
License: https://www.apache.org/licenses/LICENSE-2.0

--------------------------------------------------------------------------------

This product includes code from Conda.

* Uses deprecations.py file that's part of conda project in conda/conda/
* Uses test_deprecations.py file that's part of conda project in conda/tests/

Copyright: 2012 Anaconda, Inc.
Home page: https://conda.io/
License: https://opensource.org/licenses/BSD-3-Clause
132 changes: 113 additions & 19 deletions mkdocs/docs/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,43 +159,137 @@ Below are the formalized conventions that we adhere to in the PyIceberg project.

It is important to keep the Python public API compatible across versions. The Python official [PEP-8](https://peps.python.org/pep-0008/) defines public methods as: _Public attributes should have no leading underscores_. This means not removing any methods without any notice, or removing or renaming any existing parameters. Adding new optional parameters is okay.

If you want to remove a method, please add a deprecation notice by annotating the function using `@deprecated`:
### Functions, Methods, Properties, and Classes

To deprecate functions, methods, or properties, use the `@deprecated` decorator. This indicates that the feature will be removed in a future release and notifies users about the deprecation timeline.

#### Basic Example

```python
from pyiceberg.utils.deprecated import deprecated
from pyiceberg.utils._deprecations import deprecated

@deprecated("1.5.0", "2.0.0")
def old_function():
pass
```

This will warn:

```text
Call to old_function, deprecated in 1.5.0, will be removed in 2.0.0.
```

#### Adding a Recommendation

Optionally, include a recommendation to guide users toward an alternative feature:

```python
from pyiceberg.utils._deprecations import deprecated

@deprecated("1.5.0", "2.0.0", addendum="Use `new_function` instead.")
def old_function():
pass
```

This will warn:

```text
Call to old_function, deprecated in 1.5.0, will be removed in 2.0.0. Use `new_function` instead.
```

### Keyword Arguments

To deprecate or rename keyword arguments, use the `@deprecated.argument` decorator.

#### Deprecating a Keyword Argument

```python
from pyiceberg.utils._deprecations import deprecated

@deprecated.argument("1.5.0", "2.0.0", "old_arg")
def my_function(*, old_arg=True):
pass
```

This will warn:

```text
Call to my_function(old_arg), deprecated in 1.5.0, will be removed in 2.0.0.
```

#### Renaming a Keyword Argument

```python
from pyiceberg.utils._deprecations import deprecated

@deprecated(
deprecated_in="0.1.0",
removed_in="0.2.0",
help_message="Please use load_something_else() instead",
)
def load_something():
@deprecated.argument("1.5.0", "2.0.0", "old_arg", rename="new_arg")
def my_function(*, new_arg=True):
pass
```

Which will warn:
This will warn:

```text
Call to load_something, deprecated in 0.1.0, will be removed in 0.2.0. Please use load_something_else() instead.
Call to my_function(old_arg), deprecated in 1.5.0, will be removed in 2.0.0. Use `new_arg` instead.
```

If you want to remove a property or notify about a behavior change, please add a deprecation notice by calling the deprecation_message function:
### Constants and Enums

To deprecate constants or enums, use the `deprecated.constant` function.

#### Deprecating a Constant

```python
from pyiceberg.utils.deprecated import deprecation_message
from pyiceberg.utils._deprecations import deprecated

deprecated.constant("1.5.0", "2.0.0", "OLD_CONSTANT", 42)
```

This will warn:

```text
OLD_CONSTANT, deprecated in 1.5.0, will be removed in 2.0.0.
```

#### Deprecating an Enum

```python
from enum import Enum
from pyiceberg.utils._deprecations import deprecated

class MyEnum(Enum):
OLD_VALUE = 42

deprecated.constant("1.5.0", "2.0.0", "MyEnum", MyEnum)
del MyEnum
```

This will warn:

```text
MyEnum, deprecated in 1.5.0, will be removed in 2.0.0.
```

### Topics

For deprecations that do not fit into the above categories, use the `deprecated.topic` function.

```python
from pyiceberg.utils._deprecations import deprecated

def some_function():
# some logic

if condition:
deprecated.topic("1.5.0", "2.0.0", topic="The <TOPIC>")

deprecation_message(
deprecated_in="0.1.0",
removed_in="0.2.0",
help_message="The old_property is deprecated. Please use the something_else property instead.",
)
# more logic
```

Which will warn:
This will warn:

```text
Deprecated in 0.1.0, will be removed in 0.2.0. The old_property is deprecated. Please use the something_else property instead.
The <TOPIC>, deprecated in 1.5.0, will be removed in 2.0.0.
```

## Type annotations
Expand Down
14 changes: 3 additions & 11 deletions mkdocs/docs/how-to-release.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,13 @@ For example, the API with the following deprecation tag should be removed when p
```python

@deprecated(
deprecated_in="0.1.0",
removed_in="0.2.0",
deprecate_in="0.1.0",
remove_in="0.2.0",
help_message="Please use load_something_else() instead",
)
```

We also have the `deprecation_message` function. We need to change the behavior according to what is noted in the message of that deprecation.

```python
deprecation_message(
deprecated_in="0.1.0",
removed_in="0.2.0",
help_message="The old_property is deprecated. Please use the something_else property instead.",
)
```
We also have other deprecation strategies, so we recommend identifying all code references using the deprecation module (`pyiceberg.utils._deprecations`) and removing them before making a release.

## Running a release candidate

Expand Down
26 changes: 14 additions & 12 deletions pyiceberg/catalog/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@
Properties,
RecursiveDict,
)
from pyiceberg.utils._deprecations import deprecated
from pyiceberg.utils.config import Config, merge_config
from pyiceberg.utils.deprecated import deprecated, deprecation_message

if TYPE_CHECKING:
import pyarrow as pa
Expand Down Expand Up @@ -631,9 +631,9 @@ def drop_view(self, identifier: Union[str, Identifier]) -> None:
"""

@deprecated(
deprecated_in="0.8.0",
removed_in="0.9.0",
help_message="Support for parsing catalog level identifier in Catalog identifiers is deprecated. Please refer to the table using only its namespace and its table name.",
deprecate_in="0.8.0",
remove_in="0.9.0",
topic="Please refer to the table using only its namespace and its table name.",
)
def identifier_to_tuple_without_catalog(self, identifier: Union[str, Identifier]) -> Identifier:
"""Convert an identifier to a tuple and drop this catalog's name from the first element.
Expand All @@ -660,10 +660,11 @@ def _identifier_to_tuple_without_catalog(self, identifier: Union[str, Identifier
"""
identifier_tuple = Catalog.identifier_to_tuple(identifier)
if len(identifier_tuple) >= 3 and identifier_tuple[0] == self.name:
deprecation_message(
deprecated_in="0.8.0",
removed_in="0.9.0",
help_message="Support for parsing catalog level identifier in Catalog identifiers is deprecated. Please refer to the table using only its namespace and its table name.",
deprecated.topic(
deprecate_in="0.8.0",
remove_in="0.9.0",
prefix="Support for parsing catalog level identifier in Catalog identifiers",
topic="Please refer to the table using only its namespace and its table name.",
)
identifier_tuple = identifier_tuple[1:]
return identifier_tuple
Expand Down Expand Up @@ -782,10 +783,11 @@ def __init__(self, name: str, **properties: str):
super().__init__(name, **properties)

if self.properties.get(DEPRECATED_BOTOCORE_SESSION):
deprecation_message(
deprecated_in="0.8.0",
removed_in="0.9.0",
help_message=f"The property {DEPRECATED_BOTOCORE_SESSION} is deprecated and will be removed.",
deprecated.topic(
deprecate_in="0.8.0",
remove_in="0.9.0",
prefix=f"The property {DEPRECATED_BOTOCORE_SESSION}",
topic="and will be removed.",
)

def create_table_transaction(
Expand Down
20 changes: 11 additions & 9 deletions pyiceberg/catalog/rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
)
from pyiceberg.typedef import EMPTY_DICT, UTF8, IcebergBaseModel, Identifier, Properties
from pyiceberg.types import transform_dict_value_to_str
from pyiceberg.utils.deprecated import deprecation_message
from pyiceberg.utils._deprecations import deprecated
from pyiceberg.utils.properties import get_first_property_value, property_as_bool

if TYPE_CHECKING:
Expand Down Expand Up @@ -318,10 +318,11 @@ def url(self, endpoint: str, prefixed: bool = True, **kwargs: Any) -> str:
@property
def auth_url(self) -> str:
if self.properties.get(AUTH_URL):
deprecation_message(
deprecated_in="0.8.0",
removed_in="0.9.0",
help_message=f"The property {AUTH_URL} is deprecated. Please use {OAUTH2_SERVER_URI} instead",
deprecated.topic(
deprecate_in="0.8.0",
remove_in="0.9.0",
prefix=f"The property {AUTH_URL}",
topic=f"Please use {OAUTH2_SERVER_URI} instead",
)

self._warn_oauth_tokens_deprecation()
Expand All @@ -338,10 +339,11 @@ def _warn_oauth_tokens_deprecation(self) -> None:
has_sigv4_enabled = property_as_bool(self.properties, SIGV4, False)

if not has_oauth_server_uri and (has_init_token or has_credential) and not has_sigv4_enabled:
deprecation_message(
deprecated_in="0.8.0",
removed_in="1.0.0",
help_message="Iceberg REST client is missing the OAuth2 server URI "
deprecated.topic(
deprecate_in="0.8.0",
remove_in="1.0.0",
prefix="Default OAuth2 endpoint",
topic="Iceberg REST client is missing the OAuth2 server URI "
f"configuration and defaults to {self.uri}{Endpoints.get_token}. "
"This automatic fallback will be removed in a future Iceberg release."
f"It is recommended to configure the OAuth2 endpoint using the '{OAUTH2_SERVER_URI}'"
Expand Down
38 changes: 15 additions & 23 deletions pyiceberg/cli/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,32 +34,24 @@
from pyiceberg.exceptions import NoSuchNamespaceError, NoSuchPropertyException, NoSuchTableError
from pyiceberg.table import TableProperties
from pyiceberg.table.refs import SnapshotRef
from pyiceberg.utils.deprecated import deprecated
from pyiceberg.utils._deprecations import deprecated
from pyiceberg.utils.properties import property_as_int

deprecated.constant(
deprecate_in="0.8.0",
remove_in="0.9.0",
topic="Use TableProperties.MAX_SNAPSHOT_AGE_MS_DEFAULT instead.",
constant="DEFAULT_MAX_SNAPSHOT_AGE_MS",
value=TableProperties.MAX_SNAPSHOT_AGE_MS_DEFAULT,
)

class DeprecatedConstants:
@property
@deprecated(
deprecated_in="0.8.0",
removed_in="0.9.0",
help_message="DEFAULT_MAX_SNAPSHOT_AGE_MS is deprecated. Use TableProperties.MAX_SNAPSHOT_AGE_MS_DEFAULT instead.",
)
def DEFAULT_MAX_SNAPSHOT_AGE_MS(self) -> int:
return 432000000

@property
@deprecated(
deprecated_in="0.8.0",
removed_in="0.9.0",
help_message="DEFAULT_MIN_SNAPSHOTS_TO_KEEP is deprecated. Use TableProperties.MIN_SNAPSHOTS_TO_KEEP_DEFAULT instead.",
)
def DEFAULT_MIN_SNAPSHOTS_TO_KEEP(self) -> int:
return 1


DEFAULT_MIN_SNAPSHOTS_TO_KEEP = DeprecatedConstants().DEFAULT_MIN_SNAPSHOTS_TO_KEEP
DEFAULT_MAX_SNAPSHOT_AGE_MS = DeprecatedConstants().DEFAULT_MAX_SNAPSHOT_AGE_MS
deprecated.constant(
deprecate_in="0.8.0",
remove_in="0.9.0",
topic="Use TableProperties.MIN_SNAPSHOTS_TO_KEEP_DEFAULT instead.",
constant="DEFAULT_MIN_SNAPSHOTS_TO_KEEP",
value=TableProperties.MIN_SNAPSHOTS_TO_KEEP_DEFAULT,
)


def catch_exception() -> Callable: # type: ignore
Expand Down
11 changes: 6 additions & 5 deletions pyiceberg/expressions/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
)
from pyiceberg.typedef import L
from pyiceberg.types import strtobool
from pyiceberg.utils.deprecated import deprecation_message
from pyiceberg.utils._deprecations import deprecated

ParserElement.enablePackrat()

Expand All @@ -90,10 +90,11 @@
@column.set_parse_action
def _(result: ParseResults) -> Reference:
if len(result.column) > 1:
deprecation_message(
deprecated_in="0.8.0",
removed_in="0.9.0",
help_message="Parsing expressions with table name is deprecated. Only provide field names in the row_filter.",
deprecated.topic(
deprecate_in="0.8.0",
remove_in="0.9.0",
prefix="Parsing expressions with table name",
topic="Only provide field names in the row_filter.",
)
# TODO: Once this is removed, we will no longer take just the last index of parsed column result
# And introduce support for parsing filter expressions with nested fields.
Expand Down
Loading

0 comments on commit d2ad204

Please sign in to comment.