robotframework-tablelibrary 0.1.4__tar.gz → 0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/.pre-commit-config.yaml +13 -7
  2. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/DEVELOPMENT.md +5 -0
  3. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/PKG-INFO +2 -1
  4. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/create_release.sh +6 -1
  5. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/pyproject.toml +11 -9
  6. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/src/Tables/__about__.py +1 -1
  7. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/src/Tables/__init__.py +78 -8
  8. robotframework_tablelibrary-0.2.1/src/Tables/general/library_attributes.py +123 -0
  9. robotframework_tablelibrary-0.2.1/src/Tables/keywords/configuration.py +201 -0
  10. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/src/Tables/keywords/excel.py +2 -6
  11. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/src/Tables/keywords/getter.py +25 -27
  12. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/src/Tables/keywords/modifier.py +33 -13
  13. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/src/Tables/keywords/writer.py +8 -10
  14. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/src/Tables/utils/file_reader.py +32 -32
  15. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/src/Tables/utils/file_writer.py +63 -52
  16. robotframework_tablelibrary-0.2.1/src/Tables/utils/settings_stack.py +96 -0
  17. robotframework_tablelibrary-0.2.1/tests/__init__.py +2 -0
  18. robotframework_tablelibrary-0.2.1/tests/atest/__init__.robot +17 -0
  19. robotframework_tablelibrary-0.2.1/tests/atest/test_settings_scopes.robot +60 -0
  20. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/tests/atest/test_writer.robot +224 -0
  21. robotframework_tablelibrary-0.2.1/tests/atest/testdata/example_09_streaming.csv +3 -0
  22. robotframework_tablelibrary-0.2.1/tests/utest/__init__.py +2 -0
  23. robotframework_tablelibrary-0.2.1/tests/utest/helper/__init__.py +1 -0
  24. robotframework_tablelibrary-0.2.1/tests/utest/helper/helpers.py +40 -0
  25. robotframework_tablelibrary-0.2.1/tests/utest/keywords/__init__.py +1 -0
  26. robotframework_tablelibrary-0.2.1/tests/utest/keywords/conftest.py +31 -0
  27. robotframework_tablelibrary-0.2.1/tests/utest/keywords/test_keywords_configuration.py +51 -0
  28. robotframework_tablelibrary-0.2.1/tests/utest/keywords/test_keywords_excel.py +142 -0
  29. robotframework_tablelibrary-0.2.1/tests/utest/keywords/test_keywords_getter.py +262 -0
  30. robotframework_tablelibrary-0.2.1/tests/utest/keywords/test_keywords_init.py +7 -0
  31. robotframework_tablelibrary-0.2.1/tests/utest/keywords/test_keywords_modifier.py +49 -0
  32. robotframework_tablelibrary-0.2.1/tests/utest/keywords/test_keywords_writer.py +59 -0
  33. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/tests/utest/test_configuration.py +1 -3
  34. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/tests/utest/test_getter.py +1 -7
  35. robotframework_tablelibrary-0.2.1/tests/utest/test_tables_init.py +40 -0
  36. robotframework_tablelibrary-0.2.1/tests/utest/test_utils_file_access.py +8 -0
  37. robotframework_tablelibrary-0.2.1/tests/utest/test_utils_file_reader.py +333 -0
  38. robotframework_tablelibrary-0.2.1/tests/utest/test_utils_file_system.py +25 -0
  39. robotframework_tablelibrary-0.2.1/tests/utest/test_utils_file_writer.py +224 -0
  40. robotframework_tablelibrary-0.2.1/tests/utest/test_utils_init.py +6 -0
  41. robotframework_tablelibrary-0.2.1/tests/utest/test_utils_settings.py +23 -0
  42. robotframework_tablelibrary-0.2.1/tests/utest/test_utils_settings_stack.py +119 -0
  43. robotframework_tablelibrary-0.1.4/src/Tables/general/library_attributes.py +0 -68
  44. robotframework_tablelibrary-0.1.4/src/Tables/keywords/configuration.py +0 -119
  45. robotframework_tablelibrary-0.1.4/tests/__init__.py +0 -3
  46. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/.github/workflows/build.yml +0 -0
  47. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/.github/workflows/release.yml +0 -0
  48. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/.gitignore +0 -0
  49. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/LICENSE +0 -0
  50. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/README.md +0 -0
  51. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/src/Tables/general/__init__.py +0 -0
  52. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/src/Tables/keywords/__init__.py +0 -0
  53. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/src/Tables/utils/__init__.py +0 -0
  54. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/src/Tables/utils/file_access.py +0 -0
  55. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/src/Tables/utils/file_system.py +0 -0
  56. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/src/Tables/utils/settings.py +0 -0
  57. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/tests/atest/test_excel.robot +0 -0
  58. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/tests/atest/test_reader.robot +0 -0
  59. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/tests/atest/test_settings.robot +0 -0
  60. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/tests/atest/testdata/example_01.csv +0 -0
  61. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/tests/atest/testdata/example_01.txt +0 -0
  62. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/tests/atest/testdata/example_02.xlsx +0 -0
  63. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/tests/atest/testdata/example_03.parquet +0 -0
  64. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/tests/atest/testdata/example_04.csv +0 -0
  65. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/tests/atest/testdata/example_04.txt +0 -0
  66. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/tests/atest/testdata/example_05.parquet +0 -0
  67. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/tests/atest/testdata/example_06.xlsx +0 -0
  68. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/tests/atest/testdata/example_07.csv +0 -0
  69. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/tests/atest/testdata/example_08.csv +0 -0
  70. {robotframework_tablelibrary-0.1.4 → robotframework_tablelibrary-0.2.1}/utils/imbus-web-logo.svg +0 -0
@@ -9,6 +9,11 @@ repos:
9
9
  language: python
10
10
  types_or: [python, pyi]
11
11
  require_serial: true
12
+ args:
13
+ - --disable-error-code
14
+ - no-any-return
15
+ - --disable-error-code
16
+ - arg-type
12
17
 
13
18
  # try generating the libdoc documentation
14
19
  - repo: local
@@ -23,13 +28,6 @@ repos:
23
28
  # Ruff linter for Python files
24
29
  - repo: local
25
30
  hooks:
26
- # - id: ruff-format
27
- # name: ruff-format
28
- # description: "Run 'ruff format' for extremely fast Python formatting"
29
- # entry: ruff format --force-exclude
30
- # language: python
31
- # types_or: [python, pyi, jupyter]
32
- # require_serial: true
33
31
 
34
32
  - id: ruff
35
33
  name: ruff
@@ -39,6 +37,14 @@ repos:
39
37
  types_or: [python, pyi, jupyter]
40
38
  require_serial: true
41
39
 
40
+ - id: ruff-format
41
+ name: ruff-format
42
+ description: "Run 'ruff format' for extremely fast Python formatting"
43
+ entry: ruff format --force-exclude
44
+ language: python
45
+ types_or: [python, pyi, jupyter]
46
+ require_serial: true
47
+
42
48
  # Robocop formatter and linter for Robot Framework (.robot and .resource files)
43
49
  - repo: local
44
50
  hooks:
@@ -1,5 +1,10 @@
1
1
  # Development
2
2
 
3
+ ## Unit Testing
4
+
5
+ We are measuring the coverage of the unit tests & expect a minimum coverage of 95% - otherwise the pipeline job for the unit test execution will fail accordingly.
6
+ Please ensure you've added unit tests whenever adding new source code to the project.
7
+
3
8
  ## Release Creation
4
9
 
5
10
  ### Precondition
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: robotframework-tablelibrary
3
- Version: 0.1.4
3
+ Version: 0.2.1
4
4
  Summary: A Robot Framework library providing generic table keywords for several table file type like csv or excel.
5
5
  Project-URL: Homepage, https://github.com/imbus/robotframework-tablelibrary
6
6
  Project-URL: Documentation, https://github.com/imbus/robotframework-tablelibrary#readme
@@ -30,6 +30,7 @@ Requires-Dist: hatch; extra == 'dev'
30
30
  Requires-Dist: mypy; extra == 'dev'
31
31
  Requires-Dist: pre-commit; extra == 'dev'
32
32
  Requires-Dist: pytest; extra == 'dev'
33
+ Requires-Dist: pytest-cov; extra == 'dev'
33
34
  Requires-Dist: robotframework-robocop; extra == 'dev'
34
35
  Requires-Dist: ruff; extra == 'dev'
35
36
  Description-Content-Type: text/markdown
@@ -13,7 +13,12 @@ FILE="src/Tables/__about__.py"
13
13
  echo "🔧 Updating version to '${NEW_VERSION}' in '${FILE}'"
14
14
 
15
15
  # Example: __version__ = "0.0.6" → __version__ = "0.0.7"
16
- sed -i.bak -E "s/^(__version__\s*=\s*\")[^\"]+\"/\1${NEW_VERSION}\"/" "$FILE"
16
+ if [[ "$OSTYPE" == "darwin"* ]]; then
17
+ sed -i '' -E "s/^(__version__ *= *\")[^\"]+/\1${NEW_VERSION}/" src/Tables/__about__.py
18
+ else
19
+ sed -i.bak -E "s/^(__version__\s*=\s*\")[^\"]+\"/\1${NEW_VERSION}\"/" "$FILE"
20
+ fi
21
+
17
22
  rm -f "${FILE}.bak"
18
23
 
19
24
  git checkout main
@@ -39,6 +39,7 @@ dev = [
39
39
  "hatch",
40
40
  "ruff",
41
41
  "pytest",
42
+ "pytest-cov",
42
43
  "pre-commit",
43
44
  "mypy",
44
45
  "robotframework-robocop"
@@ -91,12 +92,13 @@ exclude_lines = [
91
92
  [tool.hatch.envs.dev]
92
93
  dependencies = [
93
94
  "ruff",
94
- "pytest"
95
+ "pytest",
96
+ "pytest-cov"
95
97
  ]
96
98
  [tool.hatch.envs.dev.scripts]
97
99
  lint = "ruff check ."
98
100
  atest = "robot tests/atest/"
99
- utest = "pytest"
101
+ utest = "pytest --cov=Tables --cov-report=term-missing --cov-branch --cov-fail-under=95"
100
102
  docs = "libdoc src/Tables results/keyword_docs.html"
101
103
 
102
104
  [tool.mypy]
@@ -110,27 +112,26 @@ warn_unreachable = true
110
112
  pretty = true
111
113
 
112
114
  [tool.ruff]
113
- fix = false
114
115
  target-version = "py310"
115
- line-length = 100
116
+ line-length = 125
116
117
  exclude = [
117
118
  "__pycache__",
118
119
  ".venv",
119
120
  "tools",
120
121
  "model",
122
+ ".git",
123
+ "build",
124
+ "dist"
121
125
  ]
122
126
 
123
127
  [tool.ruff.format]
124
128
  quote-style = "double"
129
+ indent-style = "space"
125
130
 
126
131
  [tool.ruff.lint]
127
132
  ignore = [
128
- "N815", # mixedCase variable in class scope
129
- "N803", # argument name should be lowercase
130
- "T201", # Print statements
131
- "INP001", # part of an implicit namespace package
132
133
  "N999", # Invalid module name
133
- "E501", # Line Length
134
+ "E501" # docstrings
134
135
  ]
135
136
  select = [
136
137
  "E",
@@ -159,6 +160,7 @@ select = [
159
160
  "SIM",
160
161
  "RUF",
161
162
  ]
163
+ unfixable = []
162
164
 
163
165
  [tool.robocop.lint]
164
166
  select = [
@@ -1,4 +1,4 @@
1
1
  # SPDX-FileCopyrightText: 2025-present Marvin Klerx <marvinklerx20@gmail.com>
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
- __version__ = "0.1.4"
4
+ __version__ = "0.2.1"
@@ -1,9 +1,12 @@
1
1
  # SPDX-FileCopyrightText: 2025-present Marvin Klerx <marvinklerx20@gmail.com>
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
+ from robot import result, running
4
5
  from robot.api.deco import library
5
6
  from robotlibcore import HybridCore
6
7
 
8
+ from Tables.utils.settings_stack import Scope, SettingsStack
9
+
7
10
  from .__about__ import __version__
8
11
  from .keywords import (
9
12
  Configuration,
@@ -45,6 +48,33 @@ class Tables(HybridCore):
45
48
  - latin-1
46
49
  - further more... (define your own file encoding with the given library argument)
47
50
 
51
+ == Live Data Streaming ==
52
+ Some keywords allow you to write the inserted or appended data directly into an output table file.
53
+ By default, the table library writes the modified table data first into the internal cache.
54
+ In this case, you decide when to write the data into a file.
55
+
56
+ If you require the modified data on the fly within the file on your file system, you can enable the streaming via ``Configure Streaming``.
57
+
58
+ Afterwards, every keyword that allows the on-the-fly streaming, writes the new data directly into the file system instead of modifying the table just in the cache.
59
+
60
+ Example:
61
+ | # enable the streaming on-the-fly
62
+ | Configure Streaming True Test
63
+ |
64
+ | VAR ${filepath} = ${CURDIR}/results/test_writer_live_streaming_append_row.csv
65
+ | VAR @{headers} = counter random
66
+ | ${uuid} = Tables.Create Table headers=${headers} file_path=${filepath}
67
+ |
68
+ | # append ten rows on-the-fly within the given csv file
69
+ | FOR ${i} IN RANGE ${10}
70
+ | ${random} = Generate Random String 10
71
+ | VAR @{row} = ${i} ${random}
72
+ | Tables.Append Row ${row}
73
+ |
74
+ | ${table_content} = Tables.Read Table ${filepath}
75
+ | Should Be Equal ${table_content}[-1][1] ${random}
76
+ | END
77
+
48
78
  == Excel Files ==
49
79
  We have included a basic handling of ``Excel`` files,
50
80
  but for more complex excel features, please take a look at the following library:
@@ -155,18 +185,32 @@ class Tables(HybridCore):
155
185
  | ``quoting`` | Define which values should be surrounded with quotes, please check the CSV quoting for more details. Default is ``MINIMAL`` |
156
186
  | ``quoting_character`` | Define quoting character to use for writing table files. Default is ``\"`` |
157
187
  """
158
- self._file_type = file_type
159
- self._separator = separator
160
- self._file_encoding: str = (
161
- file_encoding.value if isinstance(file_encoding, FileEncoding) else file_encoding
188
+
189
+ # required variables for SettingsScope mechanism
190
+ self.scope_stack: dict = {}
191
+ self.suite_ids: dict[str, None] = {}
192
+ self.current_test_id: str | None = None
193
+ self.is_test_case_running = False
194
+
195
+ # following config variables are using SettingsStack engine with scopes
196
+ self.scope_stack["ignore_header"] = SettingsStack(ignore_header, self)
197
+ self.scope_stack["file_type"] = SettingsStack(file_type, self)
198
+ self.scope_stack["separator"] = SettingsStack(separator, self)
199
+ self.scope_stack["line_terminator"] = SettingsStack(line_terminator, self)
200
+ self.scope_stack["file_encoding"] = SettingsStack(
201
+ file_encoding.value if isinstance(file_encoding, FileEncoding) else file_encoding, self
162
202
  )
163
- self._ignore_header = ignore_header
164
- self._line_terminator = line_terminator
165
- self._quoting = quoting
166
- self._quoting_character = quoting_character
203
+ self.scope_stack["quoting"] = SettingsStack(quoting, self)
204
+ self.scope_stack["quoting_character"] = SettingsStack(quoting_character, self)
205
+ self.scope_stack["enable_streaming"] = SettingsStack(False, self)
167
206
 
168
207
  self.file_access = FileAccess(self)
169
208
 
209
+ self.ROBOT_LIBRARY_LISTENER = [
210
+ self,
211
+ Modifier(self, self.file_access),
212
+ ]
213
+
170
214
  libraries = [
171
215
  Configuration(self),
172
216
  Getter(self, self.file_access),
@@ -176,3 +220,29 @@ class Tables(HybridCore):
176
220
  ]
177
221
 
178
222
  super().__init__(libraries)
223
+
224
+ def _start_suite(self, _name: running.TestSuite, attrs: result.TestSuite):
225
+ self.suite_ids[attrs.id] = None
226
+ self._add_to_scope_stack(attrs.id, Scope.Suite)
227
+
228
+ def _start_test(self, _name: running.TestCase, attrs: result.TestCase):
229
+ self.current_test_id = attrs.id
230
+ self._add_to_scope_stack(attrs.id, Scope.Test)
231
+ self.is_test_case_running = True
232
+
233
+ def _end_test(self, _name: running.TestCase, attrs: result.TestCase):
234
+ self._remove_from_scope_stack(attrs.id)
235
+ self.current_test_id = None
236
+ self.is_test_case_running = False
237
+
238
+ def _end_suite(self, _name: running.TestSuite, attrs: result.TestSuite):
239
+ self._remove_from_scope_stack(attrs.id)
240
+ self.suite_ids.pop(attrs.id, None)
241
+
242
+ def _add_to_scope_stack(self, scope_id: str, scope: Scope):
243
+ for stack in self.scope_stack.values():
244
+ stack.start(scope_id, scope)
245
+
246
+ def _remove_from_scope_stack(self, scope_id):
247
+ for stack in self.scope_stack.values():
248
+ stack.end(scope_id)
@@ -0,0 +1,123 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ from Tables.utils.settings import Delimiter, FileEncoding, FileType, LineTerminator, Quoting, QuotingCharacter
4
+ from Tables.utils.settings_stack import SettingsStack
5
+
6
+ if TYPE_CHECKING:
7
+ from .. import Tables
8
+
9
+
10
+ class LibraryAttributes:
11
+ def __init__(self, library: "Tables") -> None:
12
+ """
13
+ Expose library attributes to all classes
14
+ """
15
+ self.library = library
16
+
17
+ ###
18
+ ### New strategy: use SettingsStack like in Browser library for different scopes
19
+ ###
20
+
21
+ # ignore_header
22
+ @property
23
+ def ignore_header(self) -> bool:
24
+ return self.library.scope_stack["ignore_header"].get()
25
+
26
+ @property
27
+ def ignore_header_stack(self) -> SettingsStack:
28
+ return self.library.scope_stack["ignore_header"]
29
+
30
+ @ignore_header_stack.setter
31
+ def ignore_header_stack(self, stack: SettingsStack):
32
+ self.library.scope_stack["ignore_header"] = stack
33
+
34
+ # file_type
35
+ @property
36
+ def file_type(self) -> FileType:
37
+ return self.library.scope_stack["file_type"].get()
38
+
39
+ @property
40
+ def file_type_stack(self) -> SettingsStack:
41
+ return self.library.scope_stack["file_type"]
42
+
43
+ @file_type_stack.setter
44
+ def file_type_stack(self, stack: SettingsStack):
45
+ self.library.scope_stack["file_type"] = stack
46
+
47
+ # separator / delimiter
48
+ @property
49
+ def separator(self) -> Delimiter:
50
+ return self.library.scope_stack["separator"].get()
51
+
52
+ @property
53
+ def separator_stack(self) -> SettingsStack:
54
+ return self.library.scope_stack["separator"]
55
+
56
+ @separator_stack.setter
57
+ def separator_stack(self, stack: SettingsStack):
58
+ self.library.scope_stack["separator"] = stack
59
+
60
+ # line terminator
61
+ @property
62
+ def line_terminator(self) -> LineTerminator:
63
+ return self.library.scope_stack["line_terminator"].get()
64
+
65
+ @property
66
+ def line_terminator_stack(self) -> SettingsStack:
67
+ return self.library.scope_stack["line_terminator"]
68
+
69
+ @line_terminator_stack.setter
70
+ def line_terminator_stack(self, stack: SettingsStack):
71
+ self.library.scope_stack["line_terminator"] = stack
72
+
73
+ # file encoding
74
+ @property
75
+ def file_encoding(self) -> FileEncoding:
76
+ return self.library.scope_stack["file_encoding"].get()
77
+
78
+ @property
79
+ def file_encoding_stack(self) -> SettingsStack:
80
+ return self.library.scope_stack["file_encoding"]
81
+
82
+ @file_encoding_stack.setter
83
+ def file_encoding_stack(self, stack: SettingsStack):
84
+ self.library.scope_stack["file_encoding"] = stack
85
+
86
+ # quoting
87
+ @property
88
+ def quoting(self) -> Quoting:
89
+ return self.library.scope_stack["quoting"].get()
90
+
91
+ @property
92
+ def quoting_stack(self) -> SettingsStack:
93
+ return self.library.scope_stack["quoting"]
94
+
95
+ @quoting_stack.setter
96
+ def quoting_stack(self, stack: SettingsStack):
97
+ self.library.scope_stack["quoting"] = stack
98
+
99
+ # quoting character
100
+ @property
101
+ def quoting_character(self) -> QuotingCharacter:
102
+ return self.library.scope_stack["quoting_character"].get()
103
+
104
+ @property
105
+ def quoting_character_stack(self) -> SettingsStack:
106
+ return self.library.scope_stack["quoting_character"]
107
+
108
+ @quoting_character_stack.setter
109
+ def quoting_character_stack(self, stack: SettingsStack):
110
+ self.library.scope_stack["quoting_character"] = stack
111
+
112
+ # data streaming into files
113
+ @property
114
+ def enable_streaming(self) -> QuotingCharacter:
115
+ return self.library.scope_stack["enable_streaming"].get()
116
+
117
+ @property
118
+ def enable_streaming_stack(self) -> SettingsStack:
119
+ return self.library.scope_stack["enable_streaming"]
120
+
121
+ @enable_streaming_stack.setter
122
+ def enable_streaming_stack(self, stack: SettingsStack):
123
+ self.library.scope_stack["enable_streaming"] = stack
@@ -0,0 +1,201 @@
1
+ from dataclasses import asdict, dataclass
2
+
3
+ from robot.api import logger
4
+ from robot.api.deco import keyword
5
+
6
+ from ..general.library_attributes import LibraryAttributes
7
+ from ..utils.settings import (
8
+ Delimiter,
9
+ FileEncoding,
10
+ FileType,
11
+ LineTerminator,
12
+ Quoting,
13
+ QuotingCharacter,
14
+ )
15
+ from ..utils.settings_stack import Scope
16
+
17
+
18
+ @dataclass
19
+ class ConfigCtx:
20
+ file_type: FileType
21
+ separator: Delimiter
22
+ quoting: Quoting
23
+ quoting_character: QuotingCharacter
24
+ line_terminator: LineTerminator
25
+ file_encoding: FileEncoding
26
+ ignore_header: bool
27
+ enable_streaming: bool
28
+
29
+
30
+ class Configuration(LibraryAttributes):
31
+ @keyword(tags=["Configuration"])
32
+ def get_library_configuration(self) -> dict:
33
+ """
34
+ Keyword returns the current internal library configuration.
35
+
36
+ Return value is type ``ConfigCtx`` and contains all relevant configuration parameters.
37
+ """
38
+ return asdict(
39
+ ConfigCtx(
40
+ file_type=self.file_type,
41
+ separator=self.separator,
42
+ quoting=self.quoting,
43
+ quoting_character=self.quoting_character,
44
+ line_terminator=self.line_terminator,
45
+ file_encoding=self.file_encoding,
46
+ ignore_header=self.ignore_header,
47
+ enable_streaming=self.enable_streaming,
48
+ )
49
+ )
50
+
51
+ @keyword(tags=["Configuration"])
52
+ def configure_file_type(self, file_type: FileType, scope: Scope = Scope.Suite):
53
+ """
54
+ Change the internal file type during your test execution dynamically.
55
+
56
+ | =`Arguments`= | =`Description`= |
57
+ | ``file_type`` | Choose the new file type |
58
+
59
+ == Example ==
60
+ | Configure File Type CSV
61
+ | Configure File Type Excel
62
+ | Configure File Type Parquet
63
+ """
64
+ old_config = self.file_type
65
+ self.file_type_stack.set(file_type, scope)
66
+ logger.debug(f"'file_type': old value: {old_config} - new value: {file_type}")
67
+ return old_config
68
+
69
+ @keyword(tags=["Configuration"])
70
+ def configure_separator(self, separator: Delimiter, scope: Scope = Scope.Suite):
71
+ """
72
+ Change the internal separator during your test execution dynamically.
73
+
74
+ | =`Arguments`= | =`Description`= |
75
+ | ``separator`` | Define a new separator |
76
+
77
+ == Example ==
78
+ | Configure Delimiter ;
79
+ | Configure Delimiter ,
80
+ | Configure Delimiter \\t
81
+ """
82
+ old_config = self.separator
83
+ self.separator_stack.set(separator, scope)
84
+ logger.debug(f"'file_type': old value: {old_config} - new value: {separator}")
85
+ return old_config
86
+
87
+ @keyword(tags=["Configuration"])
88
+ def configure_quoting(self, quoting: Quoting, scope: Scope = Scope.Suite):
89
+ """
90
+ Change the internal quoting mode during your test execution dynamically.
91
+
92
+ | =`Arguments`= | =`Description`= |
93
+ | ``quoting`` | Define a new quoting mode |
94
+
95
+ == Example ==
96
+ | Configure Quoting MINIMAL
97
+ | Configure Quoting NONNUMERIC
98
+ | Configure Quoting NONE
99
+ """
100
+ old_config = self.quoting
101
+ self.quoting_stack.set(quoting, scope)
102
+ logger.debug(f"'file_type': old value: {old_config} - new value: {quoting}")
103
+ return old_config
104
+
105
+ @keyword(tags=["Configuration"])
106
+ def configure_quoting_character(self, quoting_character: QuotingCharacter, scope: Scope = Scope.Suite):
107
+ """
108
+ Change the internal quoting character during your test execution dynamically.
109
+
110
+ | =`Arguments`= | =`Description`= |
111
+ | ``quoting`` | Define a new quoting mode |
112
+
113
+ == Example ==
114
+ | Configure Quoting Character "
115
+ | Configure Quoting Character '
116
+ """
117
+ old_config = self.quoting_character
118
+ self.quoting_character_stack.set(quoting_character, scope)
119
+ logger.debug(f"'file_type': old value: {old_config} - new value: {quoting_character}")
120
+ return old_config
121
+
122
+ @keyword(tags=["Configuration"])
123
+ def configure_line_terminator(self, line_terminator: LineTerminator, scope: Scope = Scope.Suite):
124
+ """
125
+ Change the internal line terminator during your test execution dynamically.
126
+
127
+ | =`Arguments`= | =`Description`= |
128
+ | ``line_terminator`` | Define a new line_terminator |
129
+
130
+ == Example ==
131
+ | Configure Line Terminator LF
132
+ | Configure Line Terminator CRLF
133
+ """
134
+ old_config = self.line_terminator
135
+ self.line_terminator_stack.set(line_terminator, scope)
136
+ logger.debug(f"'file_type': old value: {old_config} - new value: {line_terminator}")
137
+ return old_config
138
+
139
+ @keyword(tags=["Configuration"])
140
+ def configure_file_encoding(self, file_encoding: FileEncoding | str, scope: Scope = Scope.Suite):
141
+ """
142
+ Change the internal file encoding during your test execution dynamically.
143
+
144
+ | =`Arguments`= | =`Description`= |
145
+ | ``file_encoding`` | Define a new file encoding |
146
+
147
+ == Example ==
148
+ | Configure File Encoding UTF_8
149
+ | Configure File Encoding UTF_16
150
+ | Configure File Encoding LATIN_1
151
+
152
+ see [Python Encoding Names|https://docs.python.org/3/library/codecs.html#standard-encodings]
153
+ """
154
+ old_config = self.file_encoding
155
+ self.file_encoding_stack.set(
156
+ file_encoding.value if isinstance(file_encoding, FileEncoding) else file_encoding, scope
157
+ )
158
+ logger.debug(f"'file_type': old value: {old_config} - new value: {file_encoding}")
159
+ return old_config
160
+
161
+ @keyword(tags=["Configuration"])
162
+ def configure_ignore_header(self, ignore_header: bool, scope: Scope = Scope.Suite):
163
+ """
164
+ Change the internal setting to (not) ignore the data header lines during your test execution dynamically.
165
+
166
+ | =`Arguments`= | =`Description`= |
167
+ | ``ignore_header`` | Ignore / recognize header columns |
168
+
169
+ == Example ==
170
+ | Configure Ignore Header True
171
+ | Configure Ignore Header False
172
+ """
173
+
174
+ old_config = self.ignore_header
175
+ self.ignore_header_stack.set(ignore_header, scope)
176
+ logger.debug(f"'ignore_header': old value: {old_config} - new value: {ignore_header}")
177
+ return old_config
178
+
179
+ @keyword(tags=["Configuration"])
180
+ def configure_streaming(self, enable_streaming: bool, scope: Scope = Scope.Suite):
181
+ """
182
+ Enable / disable the direct streaming of data into the given table file.
183
+
184
+ = Default Behaviour =
185
+ By default, you can open a table, modify the content in the internal cache of the Table library and afterwards to write it into a table file.
186
+
187
+ = Behaviour using Streaming =
188
+ Using the streaming behaviour enables you to write e.g. rows directly into the table file instead of the internal cache.
189
+ This can be required in case you need the data immediately in the file, and not in the cache.
190
+
191
+ | =`Arguments`= | =`Description`= |
192
+ | ``enable_streaming`` | Set to ``True`` to enable the streaming - default: ``False`` |
193
+
194
+ == Example ==
195
+ | Configure Streaming True
196
+ | Configure Streaming False
197
+ """
198
+ old_config = self.enable_streaming
199
+ self.enable_streaming_stack.set(enable_streaming, scope)
200
+ logger.debug(f"'file_type': old value: {old_config} - new value: {enable_streaming}")
201
+ return old_config
@@ -42,9 +42,7 @@ class Excel(LibraryAttributes):
42
42
  ###
43
43
 
44
44
  @keyword(tags=["Excel", "Getter"])
45
- def excel_open(
46
- self, alias: str, path: str, sheet_name: str | list[str | int] | None = None
47
- ) -> str:
45
+ def excel_open(self, alias: str, path: str, sheet_name: str | list[str | int] | None = None) -> str:
48
46
  """
49
47
  Keyword to open the given excel file.
50
48
 
@@ -97,9 +95,7 @@ class Excel(LibraryAttributes):
97
95
  self.current_file = None
98
96
  logger.info(f"Excel file '{alias}' is closed!")
99
97
  return True
100
- raise KeyError(
101
- f"Given file alias '{alias}' does not exist - check all opened files and their alias first!"
102
- )
98
+ raise KeyError(f"Given file alias '{alias}' does not exist - check all opened files and their alias first!")
103
99
 
104
100
  @keyword(tags=["Excel", "Getter"])
105
101
  def excel_get_open_files(self) -> list: