Skip to content

Commit fef6e9b

Browse files
committed
Release v1.6.1
1 parent 3efcc0d commit fef6e9b

File tree

7 files changed

+72
-15
lines changed

7 files changed

+72
-15
lines changed

docs/changelog.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22
Changelog
33
#########
44

5+
Version 1.6.1
6+
=============
7+
8+
* `~firebird.base.config` module:
9+
10+
- Fixed bug with `.Config.get_config()` and `plain` bool argument.
11+
- `.StrOption` now supports preservation of significant leading whitespace for multiline
12+
values (like `.PyCodeOption`).
13+
14+
515
Version 1.6.0
616
=============
717

@@ -14,6 +24,7 @@ Version 1.6.0
1424

1525
- `.Config.get_config()` and `.Option.get_config()` now provides `plain` bool argument
1626
to return configuration text without comments. Deafult is False.
27+
- `.create_config` is now deprecated, will be removed in version 2.0.
1728

1829
* `~firebird.base.trace` module:
1930

docs/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@
2323
author = 'Pavel Císař'
2424

2525
# The short X.Y version
26-
version = '1.6.0'
26+
version = '1.6.1'
2727

2828
# The full version, including alpha/beta/rc tags
29-
release = '1.6.0'
29+
release = '1.6.1'
3030

3131

3232
# -- General configuration ---------------------------------------------------

docs/config.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,5 +359,7 @@ Options
359359

360360
Functions
361361
=========
362+
.. autofunction:: has_verticals
363+
.. autofunction:: has_leading_spaces
362364
.. autofunction:: create_config
363365

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "firebird-base"
7-
version = "1.6.0"
7+
version = "1.6.1"
88
description = "Firebird base modules for Python"
99
readme = "README.rst"
1010
requires-python = ">=3.8"
@@ -26,7 +26,7 @@ classifiers = [
2626
"Topic :: Software Development",
2727
]
2828
dependencies = [
29-
"protobuf>=4.21.0, <5",
29+
"protobuf>=4.21.0, <4.22",
3030
]
3131

3232
[project.urls]

src/firebird/base/config.py

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,16 @@
6363

6464
PROTO_CONFIG = 'firebird.base.ConfigProto'
6565

66+
def has_verticals(value: str) -> bool:
67+
"Returns True if lines in multiline string contains leading '|' character."
68+
return any(1 for line in value.split('\n') if line.startswith('|'))
69+
70+
def has_leading_spaces(value: str) -> bool:
71+
"Returns True if any line in multiline string starts with space(s)."
72+
return any(1 for line in value.split('\n') if line.startswith(' '))
73+
6674
def unindent_verticals(value: str) -> str:
67-
"""Removes trailing '|' character from each line in multiline string."""
75+
"""Removes leading '|' character from each line in multiline string."""
6876
lines = []
6977
indent = None
7078
for line in value.split('\n'):
@@ -81,11 +89,13 @@ def _eq(a: Any, b: Any) -> bool:
8189

8290
def create_config(_cls: Type[Config], name: str) -> Config: # pragma: no cover
8391
"""Return newly created `Config` instance. Intended to be used with `functools.partial`.
92+
93+
.. deprecated:: 1.6
94+
Will be removed in version 2.0
8495
"""
8596
return _cls(name)
8697

8798
# Next two functions are copied from stdlib enum module, as they were removed in Python 3.11
88-
8999
def _decompose(flag, value):
90100
"""
91101
Extract all members from the value.
@@ -409,8 +419,8 @@ def get_directory_scheme(app_name: str, version: str=None, *, force_home: bool=F
409419
app_name: Application name
410420
version: Application version string
411421
force_home: When True, the general directies are always set to subdirectories of
412-
`DirectoryScheme.home` directory. When False, these the home is used
413-
ONLY when it's set by "<app_name>_HOME" environment variable.
422+
`DirectoryScheme.home` directory. When False, then home is used ONLY
423+
when it's set by "<app_name>_HOME" environment variable.
414424
"""
415425
return {'Windows': WindowsDirectoryScheme,
416426
'Linux':LinuxDirectoryScheme,
@@ -461,7 +471,7 @@ def _get_config_lines(self, plain: bool=False) -> List[str]:
461471
file processed with `~configparser.ConfigParser`.
462472
463473
Text lines with configuration start with comment marker `;` and end with newline.
464-
474+
465475
Arguments:
466476
plain: When True, it outputs only the option value. When False, it includes also
467477
option description and other helpful information.
@@ -517,7 +527,7 @@ def validate(self) -> None:
517527
def get_config(self, *, plain: bool=False) -> str:
518528
"""Returns string containing text lines suitable for use in configuration file
519529
processed with `~configparser.ConfigParser`.
520-
530+
521531
Arguments:
522532
plain: When True, it outputs only the option value. When False, it includes also
523533
option description and other helpful information.
@@ -655,7 +665,7 @@ def get_config(self, *, plain: bool=False) -> str:
655665
for config in self.configs:
656666
if not plain:
657667
lines.append('\n')
658-
lines.append(config.get_config())
668+
lines.append(config.get_config(plain=plain))
659669
return ''.join(lines)
660670
def load_config(self, config: ConfigParser, section: str=None) -> None:
661671
"""Update configuration.
@@ -738,6 +748,17 @@ def configs(self) -> List[Config]:
738748
# Options
739749
class StrOption(Option[str]):
740750
"""Configuration option with string value.
751+
752+
.. versionadded:: 1.6.1
753+
Support for verticals to preserve leading whitespace.
754+
755+
Important:
756+
Multiline string values could contain significant leading whitespace, but
757+
ConfigParser multiline string values have leading whitespace removed. To circumvent
758+
this, the `StrOption` supports assignment of text values where lines start with `|`
759+
character. This character is removed, along with any number of subsequent whitespace
760+
characters that are between `|` and first non-whitespace character on first line
761+
starting with `|`.
741762
"""
742763
def __init__(self, name: str, description: str, *, required: bool=False, default: str=None):
743764
"""
@@ -764,9 +785,10 @@ def get_formatted(self) -> str:
764785
result = self._value
765786
if '\n' in result:
766787
lines = []
788+
indent = ' | ' if has_leading_spaces(result) else ' '
767789
for line in result.splitlines(True):
768790
if lines:
769-
lines.append(' ' + line)
791+
lines.append(indent + line)
770792
else:
771793
lines.append(line)
772794
result = ''.join(lines)
@@ -780,6 +802,7 @@ def set_as_str(self, value: str) -> None:
780802
Raises:
781803
ValueError: When the argument is not a valid option value.
782804
"""
805+
value = unindent_verticals(value)
783806
self._value = value
784807
def get_as_str(self) -> str:
785808
"""Returns value as string.

src/firebird/base/types.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ def get_callable(self, arguments: str='', namespace: Dict[str, Any]=None) -> Cal
374374
return ns['expr']
375375
@property
376376
def expr(self):
377-
"Expression code ready to be appased to `eval`."
377+
"Expression code ready to be pased to `eval`."
378378
return self._expr_
379379

380380
class PyCode(str):
@@ -394,7 +394,7 @@ def __new__(cls, value: str):
394394
return new
395395
@property
396396
def code(self):
397-
"""Python code ready to be appased to `exec`.
397+
"""Python code ready to be pased to `exec`.
398398
"""
399399
return self._code_
400400

@@ -436,7 +436,8 @@ def __call__(self, *args, **kwargs):
436436
# Metaclasses
437437
def Conjunctive(name, bases, attrs):
438438
"""Returns a metaclass that is conjunctive descendant of all metaclasses used by parent
439-
classes.
439+
classes. It's necessary to create a class with multiple inheritance, where multiple
440+
parent classes use different metaclasses.
440441
441442
Example:
442443

test/test_config.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,13 @@ def setUp(self):
129129
[%(ABSENT)s]
130130
[%(BAD)s]
131131
option_name =
132+
[VERTICALS]
133+
option_name =
134+
| def pp(value):
135+
| print("Value:",value,file=output)
136+
|
137+
| for i in [1,2,3]:
138+
| pp(i)
132139
""")
133140
def test_simple(self):
134141
opt = config.StrOption('option_name', 'description')
@@ -156,6 +163,9 @@ def test_simple(self):
156163
opt.set_value(self.NEW_VAL)
157164
self.assertEqual(opt.value, self.NEW_VAL)
158165
self.assertIsInstance(opt.value, opt.datatype)
166+
# Verticals
167+
opt.load_config(self.conf, 'VERTICALS')
168+
self.assertEqual(opt.get_as_str(), '\ndef pp(value):\n print("Value:",value,file=output)\n\nfor i in [1,2,3]:\n pp(i)')
159169
def test_required(self):
160170
opt = config.StrOption('option_name', 'description', required=True)
161171
self.assertEqual(opt.name, 'option_name')
@@ -259,6 +269,16 @@ def test_get_config(self):
259269
"""
260270
opt.set_value(None)
261271
self.assertEqual(opt.get_config(), lines)
272+
lines = """; description
273+
; Type: str
274+
option_name =
275+
| def pp(value):
276+
| print("Value:",value,file=output)
277+
|
278+
| for i in [1,2,3]:
279+
| pp(i)"""
280+
opt.set_value('\ndef pp(value):\n print("Value:",value,file=output)\n\nfor i in [1,2,3]:\n pp(i)')
281+
self.assertEqual('\n'.join(x.rstrip() for x in opt.get_config().splitlines()), lines)
262282

263283
class TestIntOption(BaseConfigTest):
264284
"Unit tests for firebird.base.config.IntOption"

0 commit comments

Comments
 (0)