diff --git a/.gitignore b/.gitignore index 58629014..0b4fa311 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ # This file is part of the django-environ. # -# Copyright (c) 2021, Serghei Iakovlev +# Copyright (c) 2021-2023, Serghei Iakovlev # Copyright (c) 2013-2021, Daniele Faraglia # # For the full copyright and license information, please view diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 57c4c8dd..ef00d40a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,8 +5,19 @@ All notable changes to this project will be documented in this file. The format is inspired by `Keep a Changelog `_ and this project adheres to `Semantic Versioning `_. +`v0.11.3`_ - 0-Undefined-2023 +----------------------------- +Changed ++++++++ +- Disabled inline comments handling by default due to potential side effects. + While the feature itself is useful, the project's philosophy dictates that + it should not be enabled by default for all users + `#499 `_. + + + `v0.11.2`_ - 1-September-2023 -------------------------------- +----------------------------- Fixed +++++ - Revert "Add variable expansion." feature @@ -31,7 +42,7 @@ Added `#463 `_. - Added variable expansion `#468 `_. -- Added capability to handle comments after #, after quoted values, +- Added capability to handle comments after ``#``, after quoted values, like ``KEY= 'part1 # part2' # comment`` `#475 `_. - Added support for ``interpolate`` parameter @@ -388,6 +399,7 @@ Added - Initial release. +.. _v0.11.3: https://github.com/joke2k/django-environ/compare/v0.11.2...v0.11.3 .. _v0.11.2: https://github.com/joke2k/django-environ/compare/v0.11.1...v0.11.2 .. _v0.11.1: https://github.com/joke2k/django-environ/compare/v0.11.0...v0.11.1 .. _v0.11.0: https://github.com/joke2k/django-environ/compare/v0.10.0...v0.11.0 diff --git a/LICENSE.txt b/LICENSE.txt index 0bd208d6..8737f752 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2021, Serghei Iakovlev +Copyright (c) 2021-2023, Serghei Iakovlev Copyright (c) 2013-2021, Daniele Faraglia Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/docs/tips.rst b/docs/tips.rst index 20b8c3e1..66538c40 100644 --- a/docs/tips.rst +++ b/docs/tips.rst @@ -2,6 +2,71 @@ Tips ==== +Handling Inline Comments in .env Files +====================================== + +``django-environ`` provides an optional feature to parse inline comments in ``.env`` +files. This is controlled by the ``parse_comments`` parameter in the ``read_env`` +method. + +Modes +----- + +- **Enabled (``parse_comments=True``)**: Inline comments starting with ``#`` will be ignored. +- **Disabled (``parse_comments=False``)**: The entire line, including comments, will be read as the value. +- **Default**: The behavior is the same as when ``parse_comments=False``. + +Side Effects +------------ + +While this feature can be useful for adding context to your ``.env`` files, +it can introduce unexpected behavior. For example, if your value includes +a ``#`` symbol, it will be truncated when ``parse_comments=True``. + +Why Disabled by Default? +------------------------ + +In line with the project's philosophy of being explicit and avoiding unexpected behavior, +this feature is disabled by default. If you understand the implications and find the feature +useful, you can enable it explicitly. + +Example +------- + +Here is an example demonstrating the different modes of handling inline comments. + +**.env file contents**: + +.. code-block:: shell + + # .env file contents + BOOL_TRUE_WITH_COMMENT=True # This is a comment + STR_WITH_HASH=foo#bar # This is also a comment + +**Python code**: + +.. code-block:: python + + import environ + + # Using parse_comments=True + env = environ.Env() + env.read_env(parse_comments=True) + print(env('BOOL_TRUE_WITH_COMMENT')) # Output: True + print(env('STR_WITH_HASH')) # Output: foo + + # Using parse_comments=False + env = environ.Env() + env.read_env(parse_comments=False) + print(env('BOOL_TRUE_WITH_COMMENT')) # Output: True # This is a comment + print(env('STR_WITH_HASH')) # Output: foo#bar # This is also a comment + + # Using default behavior + env = environ.Env() + env.read_env() + print(env('BOOL_TRUE_WITH_COMMENT')) # Output: True # This is a comment + print(env('STR_WITH_HASH')) # Output: foo#bar # This is also a comment + Docker-style file based variables ================================= diff --git a/environ/__init__.py b/environ/__init__.py index 28d4a5aa..ddf05f92 100644 --- a/environ/__init__.py +++ b/environ/__init__.py @@ -21,7 +21,7 @@ __copyright__ = 'Copyright (C) 2013-2023 Daniele Faraglia' """The copyright notice of the package.""" -__version__ = '0.11.2' +__version__ = '0.11.3' """The version of the package.""" __license__ = 'MIT' diff --git a/environ/environ.py b/environ/environ.py index f74884be..a3d64f2d 100644 --- a/environ/environ.py +++ b/environ/environ.py @@ -1,6 +1,6 @@ # This file is part of the django-environ. # -# Copyright (c) 2021-2022, Serghei Iakovlev +# Copyright (c) 2021-2023, Serghei Iakovlev # Copyright (c) 2013-2021, Daniele Faraglia # # For the full copyright and license information, please view @@ -862,8 +862,8 @@ def search_url_config(cls, url, engine=None): return config @classmethod - def read_env(cls, env_file=None, overwrite=False, encoding='utf8', - **overrides): + def read_env(cls, env_file=None, overwrite=False, parse_comments=False, + encoding='utf8', **overrides): r"""Read a .env file into os.environ. If not given a path to a dotenv path, does filthy magic stack @@ -883,6 +883,8 @@ def read_env(cls, env_file=None, overwrite=False, encoding='utf8', the Django settings module from the Django project root. :param overwrite: ``overwrite=True`` will force an overwrite of existing environment variables. + :param parse_comments: Determines whether to recognize and ignore + inline comments in the .env file. Default is False. :param encoding: The encoding to use when reading the environment file. :param \**overrides: Any additional keyword arguments provided directly to read_env will be added to the environment. If the key matches an @@ -927,22 +929,40 @@ def _keep_escaped_format_characters(match): for line in content.splitlines(): m1 = re.match(r'\A(?:export )?([A-Za-z_0-9]+)=(.*)\Z', line) if m1: + + # Example: + # + # line: KEY_499=abc#def + # key: KEY_499 + # val: abc#def key, val = m1.group(1), m1.group(2) - # Look for value in quotes, ignore post-# comments - # (outside quotes) - m2 = re.match(r"\A\s*'(?