Skip to content

Commit f0bcad8

Browse files
committed
Release v1.4.2
1 parent b25a119 commit f0bcad8

File tree

8 files changed

+61
-29
lines changed

8 files changed

+61
-29
lines changed

docs/changelog.txt

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

5+
Version 1.4.2
6+
=============
7+
8+
* New `.ServerConfig` options: `~ServerConfig.encoding` and `~ServerConfig.encoding_errors`.
9+
* New `.connect_server` parameters: `encoding` and `encoding_errors`.
10+
* Requires firebird-base 1.3.1
11+
512
Version 1.4.1
613
=============
714

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.4.1'
26+
version = '1.4.2'
2727

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

3131

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

firebird/driver/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,4 @@
5959
Server, Statement
6060

6161
#: Current driver version, SEMVER string.
62-
__VERSION__ = '1.4.1'
62+
__VERSION__ = '1.4.2'

firebird/driver/config.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,13 @@ def __init__(self, name: str, *, optional: bool=False, description: str=None):
7171
#: Use trusted authentication, default: False
7272
self.trusted_auth: BoolOption = \
7373
BoolOption('trusted_auth', "Use trusted authentication", default=False)
74+
#: Encoding used for text data exchange with server
75+
self.encoding: StrOption = \
76+
StrOption('encoding', "Encoding used for text data exchange with server",
77+
default='ascii')
78+
#: Handler used for encoding errors. See `codecs error handlers <codecs>` for details.
79+
self.encoding_errors: StrOption = \
80+
StrOption('encoding_errors', "Handler used for encoding errors", default='strict')
7481

7582
class DatabaseConfig(Config):
7683
"""Database configuration.

firebird/driver/core.py

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -664,8 +664,9 @@ class SPB_ATTACH:
664664
"""
665665
def __init__(self, *, user: str = None, password: str = None, trusted_auth: bool = False,
666666
config: str = None, auth_plugin_list: str = None, expected_db: str=None,
667-
encoding: str='ascii', role: str=None):
667+
encoding: str='ascii', errors: str='strict', role: str=None):
668668
self.encoding: str = encoding
669+
self.errors: str = errors
669670
self.user: str = user
670671
self.password: str = password
671672
self.trusted_auth: bool = trusted_auth
@@ -689,38 +690,43 @@ def parse_buffer(self, buffer: bytes) -> None:
689690
while not spb.is_eof():
690691
tag = spb.get_tag()
691692
if tag == SPBItem.CONFIG:
692-
self.config = spb.get_string(encoding=self.encoding)
693+
self.config = spb.get_string(encoding=self.encoding, errors=self.errors)
693694
elif tag == SPBItem.AUTH_PLUGIN_LIST:
694695
self.auth_plugin_list = spb.get_string()
695696
elif tag == SPBItem.TRUSTED_AUTH:
696697
self.trusted_auth = True
697698
elif tag == SPBItem.USER_NAME:
698-
self.user = spb.get_string(encoding=self.encoding)
699+
self.user = spb.get_string(encoding=self.encoding, errors=self.errors)
699700
elif tag == SPBItem.PASSWORD:
700-
self.password = spb.get_string(encoding=self.encoding)
701+
self.password = spb.get_string(encoding=self.encoding, errors=self.errors)
701702
elif tag == SPBItem.SQL_ROLE_NAME:
702-
self.role = spb.get_string(encoding=self.encoding)
703+
self.role = spb.get_string(encoding=self.encoding, errors=self.errors)
703704
elif tag == SPBItem.EXPECTED_DB:
704-
self.expected_db = spb.get_string(encoding=self.encoding)
705+
self.expected_db = spb.get_string(encoding=self.encoding, errors=self.errors)
705706
def get_buffer(self) -> bytes:
706707
"""Create SPB_ATTACH from stored information.
707708
"""
708709
with a.get_api().util.get_xpb_builder(XpbKind.SPB_ATTACH) as spb:
709710
if self.config is not None:
710-
spb.insert_string(SPBItem.CONFIG, self.config, encoding=self.encoding)
711+
spb.insert_string(SPBItem.CONFIG, self.config, encoding=self.encoding,
712+
errors=self.errors)
711713
if self.trusted_auth:
712714
spb.insert_tag(SPBItem.TRUSTED_AUTH)
713715
else:
714716
if self.user is not None:
715-
spb.insert_string(SPBItem.USER_NAME, self.user, encoding=self.encoding)
717+
spb.insert_string(SPBItem.USER_NAME, self.user, encoding=self.encoding,
718+
errors=self.errors)
716719
if self.password is not None:
717-
spb.insert_string(SPBItem.PASSWORD, self.password, encoding=self.encoding)
720+
spb.insert_string(SPBItem.PASSWORD, self.password,
721+
encoding=self.encoding, errors=self.errors)
718722
if self.role is not None:
719-
spb.insert_string(SPBItem.SQL_ROLE_NAME, self.role, encoding=self.encoding)
723+
spb.insert_string(SPBItem.SQL_ROLE_NAME, self.role, encoding=self.encoding,
724+
errors=self.errors)
720725
if self.auth_plugin_list is not None:
721726
spb.insert_string(SPBItem.AUTH_PLUGIN_LIST, self.auth_plugin_list)
722727
if self.expected_db is not None:
723-
spb.insert_string(SPBItem.EXPECTED_DB, self.expected_db, encoding=self.encoding)
728+
spb.insert_string(SPBItem.EXPECTED_DB, self.expected_db,
729+
encoding=self.encoding, errors=self.errors)
724730
result = spb.get_buffer()
725731
return result
726732

@@ -5232,7 +5238,8 @@ class Server(LoggingIdMixin):
52325238
Note:
52335239
Implements context manager protocol to call `.close()` automatically.
52345240
"""
5235-
def __init__(self, svc: iService, spb: bytes, host: str, encoding: str):
5241+
def __init__(self, svc: iService, spb: bytes, host: str, encoding: str,
5242+
encoding_errors: str):
52365243
self._svc: iService = svc
52375244
#: Service Parameter Buffer (SPB) used to connect the service manager
52385245
self.spb: bytes = spb
@@ -5244,8 +5251,10 @@ def __init__(self, svc: iService, spb: bytes, host: str, encoding: str):
52445251
self.response: CBuffer = CBuffer(USHRT_MAX)
52455252
self._eof: bool = False
52465253
self.__line_buffer: List[str] = []
5247-
#: Encoding for string values
5254+
#: Encoding used for text data exchange with server
52485255
self.encoding: str = encoding
5256+
#: Handler used for encoding errors. See: `codecs#error-handlers`
5257+
self.encoding_errors: str = encoding_errors
52495258
#
52505259
self.__ev: float = None
52515260
self.__info: ServerInfoProvider = None
@@ -5308,7 +5317,7 @@ def _read_output(self, *, init: str='', timeout: int=-1) -> None:
53085317
tag = self.response.get_tag()
53095318
if tag != self.mode: # pragma: no cover
53105319
raise InterfaceError(f"Service responded with error code: {tag}")
5311-
data = self.response.read_sized_string(encoding=self.encoding)
5320+
data = self.response.read_sized_string(encoding=self.encoding, errors=self.encoding_errors)
53125321
init += data
53135322
if data and self.mode is SrvInfoCode.LINE:
53145323
init += '\n'
@@ -5438,7 +5447,8 @@ def user(self) -> ServerUserServices:
54385447

54395448
def connect_server(server: str, *, user: str=None, password: str=None,
54405449
crypt_callback: iCryptKeyCallbackImpl=None,
5441-
expected_db: str=None, role: str=None, encoding: str='ascii') -> Server:
5450+
expected_db: str=None, role: str=None, encoding: str=None,
5451+
encoding_errors: str=None) -> Server:
54425452
"""Establishes a connection to server's service manager.
54435453
54445454
Arguments:
@@ -5449,7 +5459,10 @@ def connect_server(server: str, *, user: str=None, password: str=None,
54495459
expected_db: Database that would be accessed (for using services with non-default
54505460
security database)
54515461
role: SQL role used for connection.
5452-
encoding: Encoding for string values passed in parameter buffer.
5462+
encoding: Encoding for string values passed in parameter buffer. Default is
5463+
`.ServerConfig.encoding`.
5464+
encoding_errors: Error handler used for encoding errors. Default is
5465+
`.ServerConfig.encoding_errors`.
54535466
54545467
Hooks:
54555468
Event `.ServerHook.ATTACHED`: Executed before `Service` instance is
@@ -5484,7 +5497,8 @@ def connect_server(server: str, *, user: str=None, password: str=None,
54845497
if crypt_callback is not None:
54855498
provider.set_dbcrypt_callback(crypt_callback)
54865499
svc = provider.attach_service_manager(host, spb_buf)
5487-
con = Server(svc, spb_buf, host, encoding)
5500+
con = Server(svc, spb_buf, host, srv_config.encoding.value if encoding is None else encoding,
5501+
srv_config.encoding_errors.value if encoding_errors is None else encoding_errors)
54885502
for hook in get_callbacks(ServerHook.ATTACHED, con):
54895503
hook(con)
54905504
return con

firebird/driver/interfaces.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1149,9 +1149,9 @@ def insert_bytes(self, tag: int, value: bytes) -> None:
11491149
"Inserts a clumplet with value containing passed bytes."
11501150
self.vtable.insertBytes(self, self.status, tag, value, len(value))
11511151
self._check()
1152-
def insert_string(self, tag: int, value: str, *, encoding='ascii') -> None:
1152+
def insert_string(self, tag: int, value: str, *, encoding: str='ascii', errors: str='strict') -> None:
11531153
"Inserts a clumplet with value containing passed string."
1154-
self.vtable.insertString(self, self.status, tag, value.encode(encoding))
1154+
self.vtable.insertString(self, self.status, tag, value.encode(encoding, errors))
11551155
self._check()
11561156
def insert_tag(self, tag: int) -> None:
11571157
"Inserts a clumplet without a value."
@@ -1200,11 +1200,11 @@ def get_bigint(self) -> int:
12001200
result = self.vtable.getBigInt(self, self.status)
12011201
self._check()
12021202
return result
1203-
def get_string(self, *, encoding='ascii') -> str:
1203+
def get_string(self, *, encoding: str='ascii', errors: str='strict') -> str:
12041204
"Returns value of current clumplet as string."
12051205
result = self.vtable.getString(self, self.status)
12061206
self._check()
1207-
return string_at(result).decode(encoding)
1207+
return string_at(result).decode(encoding, errors)
12081208
def get_bytes(self) -> bytes:
12091209
"Returns value of current clumplet as bytes."
12101210
buffer = self.vtable.getBytes(self, self.status)

setup.cfg

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ all-files=True
55

66
[metadata]
77
name = firebird-driver
8-
version = 1.4.1
8+
version = 1.4.2
99
description = Firebird driver
1010
long_description = file: README.rst
1111
long_description_content_type = text/x-rst; charset=UTF-8
@@ -36,7 +36,7 @@ classifiers =
3636
zip_safe = True
3737
python_requires = >=3.8, <4
3838
install_requires =
39-
firebird-base>=1.3.0
39+
firebird-base>=1.3.1
4040
python-dateutil>=2.8
4141
packages = find_namespace:
4242

test/test_driver.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,9 @@ def setUp(self):
658658
super().setUp()
659659
self.dbfile = os.path.join(self.dbpath, self.FBTEST_DB)
660660
self.db1 = os.path.join(self.dbpath, 'fbtest-1.fdb')
661-
cfg = driver_config.register_database('dts-1')
661+
cfg = driver_config.get_database('dts-1')
662+
if cfg is None:
663+
cfg = driver_config.register_database('dts-1')
662664
cfg.server.value = 'FBTEST_HOST'
663665
cfg.database.value = self.db1
664666
cfg.no_linger.value = True
@@ -668,7 +670,9 @@ def setUp(self):
668670
self.con1.commit()
669671

670672
self.db2 = os.path.join(self.dbpath, 'fbtest-2.fdb')
671-
cfg = driver_config.register_database('dts-2')
673+
cfg = driver_config.get_database('dts-2')
674+
if cfg is None:
675+
cfg = driver_config.register_database('dts-2')
672676
cfg.server.value = 'FBTEST_HOST'
673677
cfg.database.value = self.db2
674678
cfg.no_linger.value = True
@@ -1567,7 +1571,7 @@ def test_running(self):
15671571
svc.info.get_log()
15681572
self.assertTrue(svc.is_running())
15691573
# fetch materialized
1570-
svc.readlines()
1574+
print(''.join(svc.readlines()))
15711575
self.assertFalse(svc.is_running())
15721576
def test_wait(self):
15731577
with connect_server(FBTEST_HOST, user='SYSDBA', password=FBTEST_PASSWORD) as svc:

0 commit comments

Comments
 (0)