Skip to content

Commit

Permalink
Make it possible to use Kedro after uninstalling Rich. (#3838)
Browse files Browse the repository at this point in the history
* attempt to configure fallback logger

Signed-off-by: lrcouto <[email protected]>

* Set up fallback for logging on the logging function itself

Signed-off-by: lrcouto <[email protected]>

* Lint

Signed-off-by: lrcouto <[email protected]>

* Lint

Signed-off-by: lrcouto <[email protected]>

* Update kedro/logging.py

Co-authored-by: Sajid Alam <[email protected]>
Signed-off-by: L. R. Couto <[email protected]>

* Changes to rich variable on logging

Signed-off-by: lrcouto <[email protected]>

* Lint

Signed-off-by: lrcouto <[email protected]>

* attempt to configure fallback logger

Signed-off-by: lrcouto <[email protected]>

* Set up fallback for logging on the logging function itself

Signed-off-by: lrcouto <[email protected]>

* Lint

Signed-off-by: lrcouto <[email protected]>

* Lint

Signed-off-by: lrcouto <[email protected]>

* Update kedro/logging.py

Co-authored-by: Sajid Alam <[email protected]>
Signed-off-by: L. R. Couto <[email protected]>

* Changes to rich variable on logging

Signed-off-by: lrcouto <[email protected]>

* Lint

Signed-off-by: lrcouto <[email protected]>

* adapt default logging

Signed-off-by: lrcouto <[email protected]>

* Update kedro/logging.py

Co-authored-by: Juan Luis Cano Rodríguez <[email protected]>
Signed-off-by: L. R. Couto <[email protected]>

* Create separate logging config file for rich

Signed-off-by: lrcouto <[email protected]>

* Hello linter my old friend

Signed-off-by: lrcouto <[email protected]>

* Alternative for rich in kedro ipython

Signed-off-by: lrcouto <[email protected]>

* Remove unnecessary try/except block

Signed-off-by: lrcouto <[email protected]>

* Update pyproject.toml

Co-authored-by: Juan Luis Cano Rodríguez <[email protected]>
Signed-off-by: L. R. Couto <[email protected]>

* Resolve merge conflict

Signed-off-by: lrcouto <[email protected]>

* Lint

Signed-off-by: lrcouto <[email protected]>

* Fix config file paths

Signed-off-by: lrcouto <[email protected]>

* Update kedro/ipython/__init__.py

Co-authored-by: Deepyaman Datta <[email protected]>
Signed-off-by: L. R. Couto <[email protected]>

* Update kedro/ipython/__init__.py

Co-authored-by: Deepyaman Datta <[email protected]>
Signed-off-by: L. R. Couto <[email protected]>

* Prevent kedro ipython from reimporting rich multiple times

Signed-off-by: lrcouto <[email protected]>

* Logging config

Signed-off-by: lrcouto <[email protected]>

* Change test config

Signed-off-by: lrcouto <[email protected]>

* Remove '  yEs  ' tests

Signed-off-by: lrcouto <[email protected]>

* Tests to cover ipython changes

Signed-off-by: lrcouto <[email protected]>

* Add test for import

Signed-off-by: lrcouto <[email protected]>

* Ignore test coverage on import try/except

Signed-off-by: lrcouto <[email protected]>

* Lint

Signed-off-by: lrcouto <[email protected]>

* Unpin cookiecutter version

Signed-off-by: lrcouto <[email protected]>

* Add changes to documentation

Signed-off-by: lrcouto <[email protected]>

* Update RELEASE.md

Co-authored-by: Merel Theisen <[email protected]>
Signed-off-by: L. R. Couto <[email protected]>

* Change RICH variable name to RICH_INTALLED

Signed-off-by: lrcouto <[email protected]>

* Add info about uninstalling rich on logging doc page

Signed-off-by: lrcouto <[email protected]>

---------

Signed-off-by: lrcouto <[email protected]>
Signed-off-by: L. R. Couto <[email protected]>
Co-authored-by: Sajid Alam <[email protected]>
Co-authored-by: Juan Luis Cano Rodríguez <[email protected]>
Co-authored-by: Deepyaman Datta <[email protected]>
Co-authored-by: Merel Theisen <[email protected]>
  • Loading branch information
5 people authored May 21, 2024
1 parent 8eff357 commit a53500a
Show file tree
Hide file tree
Showing 13 changed files with 118 additions and 20 deletions.
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
include README.md
include LICENSE.md
include kedro/framework/project/default_logging.yml
include kedro/framework/project/rich_logging.yml
include kedro/ipython/*.png
include kedro/ipython/*.svg
recursive-include kedro/templates *
1 change: 1 addition & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Upcoming Release 0.19.6

## Major features and improvements
* It is now possible to use Kedro without having `rich` installed.

## Bug fixes and other changes
* User defined catch-all dataset factory patterns now override the default pattern provided by the runner.
Expand Down
1 change: 1 addition & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@
# "anchor not found" but it's a valid selector for code examples
"https://docs.delta.io/latest/delta-update.html#language-python",
"https://github.com/kedro-org/kedro/blob/main/kedro/framework/project/default_logging.yml",
"https://github.com/kedro-org/kedro/blob/main/kedro/framework/project/rich_logging.yml",
"https://github.com/kedro-org/kedro/blob/main/README.md#the-humans-behind-kedro", # "anchor not found" but is valid
"https://opensource.org/license/apache2-0-php/",
"https://docs.github.com/en/rest/overview/other-authentication-methods#via-username-and-password",
Expand Down
18 changes: 18 additions & 0 deletions docs/source/configuration/configuration_basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,3 +241,21 @@ Customise the configuration loader arguments in `settings.py` as follows if your
```python
CONFIG_LOADER_ARGS = {"default_run_env": "base"}
```

### How to use Kedro without the rich library

If you prefer not to have the `rich` library in your Kedro project, you have the option to uninstall it. However, it's important to note that versions of the `cookiecutter` library above 2.3 have a dependency on rich. You will need to downgrade `cookiecutter` to a version below 2.3 to have Kedro work without `rich`.

To uninstall the rich library, run:

```bash
pip uninstall rich
```

To downgrade cookiecutter to a version that does not require rich, you can specify a version below 2.3. For example:

```bash
pip install cookiecutter==2.2.0
```

These changes will affect the visual appearance and formatting of Kedro's logging, prompts, and the output of the `kedro ipython` command. While using a version of `cookiecutter` below 2.3, the appearance of the prompts will be plain even with `rich` installed.
1 change: 1 addition & 0 deletions docs/source/faq/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ This is a growing set of technical FAQs. The [product FAQs on the Kedro website]
* [How do I specify additional configuration environments](../configuration/configuration_basics.md#how-to-specify-additional-configuration-environments)?
* [How do I change the default overriding configuration environment](../configuration/configuration_basics.md#how-to-change-the-default-overriding-environment)?
* [How do I use only one configuration environment](../configuration/configuration_basics.md#how-to-use-only-one-configuration-environment)?
* [How do I use Kedro without the rich library](../configuration/configuration_basics.md#how-to-use-kedro-without-the-rich-library)?

### Advanced topics

Expand Down
18 changes: 18 additions & 0 deletions docs/source/logging/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,21 @@ You must provide a value for both `COLUMNS` and `LINES` even if you only wish to
## How to enable rich logging in Jupyter

Rich also formats the logs in JupyterLab and Jupyter Notebook. The size of the output console does not adapt to your window but can be controlled through the `JUPYTER_COLUMNS` and `JUPYTER_LINES` environment variables. The default values (115 and 100 respectively) should be suitable for most users, but if you require a different output console size then you should alter the values of `JUPYTER_COLUMNS` and `JUPYTER_LINES`.

### How to use logging without the rich library

If you prefer not to have the `rich` library in your Kedro project, you have the option to uninstall it. However, it's important to note that versions of the `cookiecutter` library above 2.3 have a dependency on rich. You will need to downgrade `cookiecutter` to a version below 2.3 to have Kedro work without `rich`.

To uninstall the rich library, run:

```bash
pip uninstall rich
```

To downgrade cookiecutter to a version that does not require rich, you can specify a version below 2.3. For example:

```bash
pip install cookiecutter==2.2.0
```

These changes will affect the visual appearance and formatting of Kedro's logging, prompts, and the output of the `kedro ipython` command. While using a version of `cookiecutter` below 2.3, the appearance of the prompts will be plain even with `rich` installed.
9 changes: 8 additions & 1 deletion kedro/framework/project/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,14 @@ def __init__(self) -> None:
# Check if the default logging configuration exists
default_logging_path = Path("conf/logging.yml")
if not default_logging_path.exists():
default_logging_path = Path(__file__).parent / "default_logging.yml"
default_logging_path = Path(
os.environ.get(
"KEDRO_LOGGING_CONFIG",
Path(__file__).parent / "rich_logging.yml"
if importlib.util.find_spec("rich")
else Path(__file__).parent / "default_logging.yml",
)
)

# Use the user path if available, otherwise, use the default path
if user_logging_path and Path(user_logging_path).exists():
Expand Down
27 changes: 20 additions & 7 deletions kedro/framework/project/default_logging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,30 @@ version: 1

disable_existing_loggers: False

formatters:
simple:
format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"

handlers:
rich:
class: kedro.logging.RichHandler
rich_tracebacks: True
# Advance options for customisation.
# See https://docs.kedro.org/en/stable/logging/logging.html#project-side-logging-configuration
# tracebacks_show_locals: False
console:
class: logging.StreamHandler
level: INFO
formatter: simple
stream: ext://sys.stdout

info_file_handler:
class: logging.handlers.RotatingFileHandler
level: INFO
formatter: simple
filename: info.log
maxBytes: 10485760 # 10MB
backupCount: 20
encoding: utf8
delay: True

loggers:
kedro:
level: INFO

root:
handlers: [rich]
handlers: [console]
18 changes: 18 additions & 0 deletions kedro/framework/project/rich_logging.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
version: 1

disable_existing_loggers: False

handlers:
rich:
class: kedro.logging.RichHandler
rich_tracebacks: True
# Advance options for customisation.
# See https://docs.kedro.org/en/stable/logging/logging.html#project-side-logging-configuration
# tracebacks_show_locals: False

loggers:
kedro:
level: INFO

root:
handlers: [rich]
21 changes: 17 additions & 4 deletions kedro/ipython/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from __future__ import annotations

import importlib
import inspect
import logging
import os
Expand All @@ -18,8 +19,12 @@
from IPython.core.getipython import get_ipython
from IPython.core.magic import needs_local_scope, register_line_magic
from IPython.core.magic_arguments import argument, magic_arguments, parse_argstring
from rich.console import Console
from rich.syntax import Syntax

try:
import rich.console as rich_console
import rich.syntax as rich_syntax
except ImportError: # pragma: no cover
pass

from kedro.framework.cli import load_entry_points
from kedro.framework.cli.project import CONF_SOURCE_HELP, PARAMS_ARG_HELP
Expand All @@ -39,6 +44,8 @@

FunctionParameters = MappingProxyType

RICH_INSTALLED = True if importlib.util.find_spec("rich") is not None else False


def load_ipython_extension(ipython: Any) -> None:
"""
Expand Down Expand Up @@ -281,8 +288,14 @@ def _create_cell_with_text(text: str, is_jupyter: bool = True) -> None:

def _print_cells(cells: list[str]) -> None:
for cell in cells:
Console().print("")
Console().print(Syntax(cell, "python", theme="monokai", line_numbers=False))
if RICH_INSTALLED is True:
rich_console.Console().print("")
rich_console.Console().print(
rich_syntax.Syntax(cell, "python", theme="monokai", line_numbers=False)
)
else:
print("") # noqa: T201
print(cell) # noqa: T201


def _load_node(node_name: str, pipelines: _ProjectPipelines) -> list[str]:
Expand Down
4 changes: 0 additions & 4 deletions kedro/logging.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
"""
This module contains a logging handler class which produces coloured logs and tracebacks.
"""

import logging
import sys
from pathlib import Path
Expand Down
4 changes: 2 additions & 2 deletions tests/framework/cli/test_starters.py
Original file line number Diff line number Diff line change
Expand Up @@ -1119,7 +1119,7 @@ def test_invalid_tools_range(self, fake_kedro_cli, input):
message = f"'{input}' is an invalid range for project tools.\nPlease ensure range values go from smaller to larger."
assert message in result.output

@pytest.mark.parametrize("example_pipeline", ["y", "n", "N", "YEs", " yeS "])
@pytest.mark.parametrize("example_pipeline", ["y", "n", "N", "YEs", " yEs "])
def test_valid_example(self, fake_kedro_cli, example_pipeline):
result = CliRunner().invoke(
fake_kedro_cli,
Expand Down Expand Up @@ -1285,7 +1285,7 @@ def test_invalid_tools_flag_combination(self, fake_kedro_cli, input):
in result.output
)

@pytest.mark.parametrize("example_pipeline", ["y", "n", "N", "YEs", " yeS "])
@pytest.mark.parametrize("example_pipeline", ["y", "n", "N", "YEs", " yEs "])
def test_valid_example(self, fake_kedro_cli, example_pipeline):
"""Test project created from config."""
config = {
Expand Down
15 changes: 13 additions & 2 deletions tests/ipython/test_ipython.py
Original file line number Diff line number Diff line change
Expand Up @@ -457,9 +457,20 @@ def test_load_node_with_ipython(self, mocker, ipython, run_env):
ipython.magic("load_node dummy_node")
spy.assert_called_once()

@pytest.mark.parametrize("run_env", ["databricks", "colab", "dummy"])
def test_load_node_with_other(self, mocker, ipython, run_env):
@pytest.mark.parametrize(
"run_env, rich_installed",
[
("databricks", True),
("databricks", False),
("colab", True),
("colab", False),
("dummy", True),
("dummy", False),
],
)
def test_load_node_with_other(self, mocker, ipython, run_env, rich_installed):
mocker.patch("kedro.ipython._find_kedro_project")
mocker.patch("kedro.ipython.RICH_INSTALLED", rich_installed)
mocker.patch("kedro.ipython._load_node", return_value=["cell1", "cell2"])
mocker.patch("kedro.ipython._guess_run_environment", return_value=run_env)
spy = mocker.spy(kedro.ipython, "_print_cells")
Expand Down

0 comments on commit a53500a

Please sign in to comment.