robotframework-tablelibrary 0.2.1__tar.gz → 0.3.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.
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/.github/workflows/build.yml +11 -52
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/.github/workflows/release.yml +4 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/.gitignore +1 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/DEVELOPMENT.md +31 -21
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/PKG-INFO +15 -2
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/README.md +11 -0
- robotframework_tablelibrary-0.3.1/create_release.sh +32 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/pyproject.toml +26 -3
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/src/Tables/__init__.py +13 -7
- robotframework_tablelibrary-0.3.1/src/Tables/_version.py +24 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/src/Tables/general/library_attributes.py +13 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/src/Tables/keywords/configuration.py +25 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/src/Tables/keywords/getter.py +21 -4
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/src/Tables/utils/file_reader.py +18 -4
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/src/Tables/utils/file_writer.py +2 -2
- robotframework_tablelibrary-0.3.1/tests/atest/test_missing_values.robot +31 -0
- robotframework_tablelibrary-0.3.1/tests/atest/testdata/missing_values.csv +3 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/utest/helper/helpers.py +3 -1
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/utest/keywords/test_keywords_configuration.py +15 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/utest/keywords/test_keywords_getter.py +82 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/utest/test_tables_init.py +3 -0
- robotframework_tablelibrary-0.3.1/uv.lock +2267 -0
- robotframework_tablelibrary-0.2.1/create_release.sh +0 -32
- robotframework_tablelibrary-0.2.1/src/Tables/__about__.py +0 -4
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/.pre-commit-config.yaml +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/LICENSE +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/src/Tables/general/__init__.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/src/Tables/keywords/__init__.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/src/Tables/keywords/excel.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/src/Tables/keywords/modifier.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/src/Tables/keywords/writer.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/src/Tables/utils/__init__.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/src/Tables/utils/file_access.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/src/Tables/utils/file_system.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/src/Tables/utils/settings.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/src/Tables/utils/settings_stack.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/__init__.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/atest/__init__.robot +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/atest/test_excel.robot +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/atest/test_reader.robot +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/atest/test_settings.robot +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/atest/test_settings_scopes.robot +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/atest/test_writer.robot +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/atest/testdata/example_01.csv +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/atest/testdata/example_01.txt +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/atest/testdata/example_02.xlsx +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/atest/testdata/example_03.parquet +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/atest/testdata/example_04.csv +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/atest/testdata/example_04.txt +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/atest/testdata/example_05.parquet +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/atest/testdata/example_06.xlsx +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/atest/testdata/example_07.csv +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/atest/testdata/example_08.csv +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/atest/testdata/example_09_streaming.csv +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/utest/__init__.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/utest/helper/__init__.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/utest/keywords/__init__.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/utest/keywords/conftest.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/utest/keywords/test_keywords_excel.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/utest/keywords/test_keywords_init.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/utest/keywords/test_keywords_modifier.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/utest/keywords/test_keywords_writer.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/utest/test_configuration.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/utest/test_getter.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/utest/test_utils_file_access.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/utest/test_utils_file_reader.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/utest/test_utils_file_system.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/utest/test_utils_file_writer.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/utest/test_utils_init.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/utest/test_utils_settings.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/tests/utest/test_utils_settings_stack.py +0 -0
- {robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/utils/imbus-web-logo.svg +0 -0
{robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/.github/workflows/build.yml
RENAMED
|
@@ -40,7 +40,8 @@ jobs:
|
|
|
40
40
|
runs-on: ubuntu-latest
|
|
41
41
|
strategy:
|
|
42
42
|
matrix:
|
|
43
|
-
python-version: ["3.10", "3.11", "3.12"]
|
|
43
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
44
|
+
robotframework: ["7.0", "7.4.2"]
|
|
44
45
|
steps:
|
|
45
46
|
- uses: actions/checkout@v4
|
|
46
47
|
|
|
@@ -53,64 +54,22 @@ jobs:
|
|
|
53
54
|
run: pip install hatch
|
|
54
55
|
|
|
55
56
|
- name: Install dependencies
|
|
56
|
-
run: hatch env create
|
|
57
|
-
|
|
58
|
-
- name: Build wheel with Hatch
|
|
59
|
-
run: hatch build
|
|
60
|
-
|
|
61
|
-
- name: Install built wheel
|
|
62
|
-
run: pip install dist/*.whl
|
|
63
|
-
|
|
64
|
-
- name: Test import
|
|
65
|
-
run: python -c "import Tables"
|
|
57
|
+
run: hatch env create test.py${{ matrix.python-version }}-${{ matrix.robotframework }}
|
|
66
58
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
steps:
|
|
71
|
-
- name: Checkout code
|
|
72
|
-
uses: actions/checkout@v4
|
|
59
|
+
- name: Show installed Python version
|
|
60
|
+
run: hatch run test.py${{ matrix.python-version }}-${{ matrix.robotframework }}:python --version
|
|
73
61
|
|
|
74
|
-
- name:
|
|
75
|
-
|
|
76
|
-
with:
|
|
77
|
-
python-version: '3.12'
|
|
78
|
-
|
|
79
|
-
- name: Install Hatch
|
|
80
|
-
run: pip install hatch
|
|
81
|
-
|
|
82
|
-
- name: Install dependencies
|
|
83
|
-
run: hatch env create dev
|
|
62
|
+
- name: Show installed Robot Framework version
|
|
63
|
+
run: hatch run test.py${{ matrix.python-version }}-${{ matrix.robotframework }}:robot --version || test $? -eq 251
|
|
84
64
|
|
|
85
65
|
- name: Build wheel with Hatch
|
|
86
66
|
run: hatch build
|
|
87
67
|
|
|
88
|
-
- name: Run
|
|
89
|
-
run: hatch run
|
|
90
|
-
|
|
91
|
-
run-unit-tests:
|
|
92
|
-
runs-on: ubuntu-latest
|
|
93
|
-
|
|
94
|
-
steps:
|
|
95
|
-
- name: Checkout code
|
|
96
|
-
uses: actions/checkout@v4
|
|
97
|
-
|
|
98
|
-
- name: Set up Python
|
|
99
|
-
uses: actions/setup-python@v5
|
|
100
|
-
with:
|
|
101
|
-
python-version: '3.12'
|
|
102
|
-
|
|
103
|
-
- name: Install Hatch
|
|
104
|
-
run: pip install hatch
|
|
105
|
-
|
|
106
|
-
- name: Install dependencies
|
|
107
|
-
run: hatch env create dev
|
|
108
|
-
|
|
109
|
-
- name: Build wheel with Hatch
|
|
110
|
-
run: hatch build
|
|
68
|
+
- name: Run unit tests
|
|
69
|
+
run: hatch run test.py${{ matrix.python-version }}-${{ matrix.robotframework }}:utest
|
|
111
70
|
|
|
112
|
-
- name: Run
|
|
113
|
-
run: hatch run
|
|
71
|
+
- name: Run acceptance tests
|
|
72
|
+
run: hatch run test.py${{ matrix.python-version }}-${{ matrix.robotframework }}:atest
|
|
114
73
|
|
|
115
74
|
generate-libdoc-docs:
|
|
116
75
|
runs-on: ubuntu-latest
|
|
@@ -14,6 +14,8 @@ jobs:
|
|
|
14
14
|
steps:
|
|
15
15
|
- name: Checkout code
|
|
16
16
|
uses: actions/checkout@v4
|
|
17
|
+
with:
|
|
18
|
+
fetch-depth: 0
|
|
17
19
|
|
|
18
20
|
- name: Create GitHub Release
|
|
19
21
|
uses: softprops/action-gh-release@v2
|
|
@@ -31,6 +33,8 @@ jobs:
|
|
|
31
33
|
steps:
|
|
32
34
|
- name: Checkout code
|
|
33
35
|
uses: actions/checkout@v4
|
|
36
|
+
with:
|
|
37
|
+
fetch-depth: 0
|
|
34
38
|
|
|
35
39
|
- name: Set up Python
|
|
36
40
|
uses: actions/setup-python@v5
|
|
@@ -20,19 +20,19 @@ In the ``root directory`` of this repository you will find a script called ``cre
|
|
|
20
20
|
When executing this script, it expects exactly one argument: the new name of the release / tag.
|
|
21
21
|
Here you need to provide always the syntax ``X.X.X``, e.g. ``0.1.9``, ``0.5.0`` or ``1.0.0``.
|
|
22
22
|
|
|
23
|
-
The
|
|
24
|
-
1. Updating the version string in the **[\_\_about\_\_.py](src/Tables/__about__.py)**
|
|
25
|
-
2. Creating & Pushing a new tag to GitHub.
|
|
23
|
+
The package version is read from the Git tag. No version file needs to be changed or committed. Create and push a tag from the desired commit:
|
|
26
24
|
|
|
27
|
-
Execute the following command to create a new release:
|
|
28
25
|
```
|
|
29
26
|
cd <project-root-directory>
|
|
30
|
-
|
|
27
|
+
git checkout main
|
|
28
|
+
git pull origin main
|
|
29
|
+
git tag -a v1.0.0 -m "Release v1.0.0"
|
|
30
|
+
git push origin v1.0.0
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
After
|
|
33
|
+
After pushing the tag, three pipeline jobs are triggered automatically:
|
|
34
34
|
1. First job creates a new ``Release`` in github with the name of the created ``Tag``.
|
|
35
|
-
2. Second job
|
|
35
|
+
2. Second job builds the wheel with the version from the Git tag and uploads it to ``PyPi``.
|
|
36
36
|
3. Third job generates a new keyword documentation via ``libdoc`` on the main branch, but pushes it to the ``gh_pages`` where it is available as public ``GitHub Page``.
|
|
37
37
|
|
|
38
38
|
### *Backup* - Manual Version Bump & Tag Creation
|
|
@@ -40,29 +40,21 @@ After executing the script, three pipeline jobs are getting triggered automatica
|
|
|
40
40
|
If the preferred way using the ``create_release.sh`` script does not work, you can also manually create a new release.
|
|
41
41
|
Therefore, execute the following steps.
|
|
42
42
|
|
|
43
|
-
####
|
|
43
|
+
#### Create a new tag
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
The new version **must be unique** & **not already existing** in ``PyPi`` & ``GitHub Releases``!!!
|
|
48
|
-
|
|
49
|
-
Commit & push the changed version into the ``main`` branch of the repository.
|
|
50
|
-
|
|
51
|
-
#### 2. Create new Tag
|
|
52
|
-
|
|
53
|
-
Now, create a new tag from the main branch with the syntax ``vX.X.X`` -> example: ``v1.0.5``.
|
|
45
|
+
Create a new tag from the desired commit with the syntax ``vX.X.X``. The version must be unique in PyPI and GitHub Releases.
|
|
54
46
|
|
|
55
47
|
Use the following commands to create & push the tag:
|
|
56
48
|
```
|
|
57
|
-
git tag
|
|
58
|
-
git push origin
|
|
49
|
+
git tag -a v1.0.0 -m "Release v1.0.0"
|
|
50
|
+
git push origin v1.0.0
|
|
59
51
|
```
|
|
60
52
|
|
|
61
|
-
####
|
|
53
|
+
#### Creating GitHub Release & Deploy Wheel Package to PyPi
|
|
62
54
|
|
|
63
55
|
After pushing the new tag, three pipeline jobs are getting triggered automatically:
|
|
64
56
|
1. First job creates a new ``Release`` in github with the name of the created ``Tag``.
|
|
65
|
-
2. Second job
|
|
57
|
+
2. Second job builds the wheel with the version from the Git tag and uploads it to ``PyPi``.
|
|
66
58
|
3. Third job generates a new keyword documentation via ``libdoc`` on the main branch, but pushes it to the ``gh_pages`` where it is available as public ``GitHub Page``.
|
|
67
59
|
|
|
68
60
|
## Installing Dev Dependencies
|
|
@@ -97,6 +89,24 @@ hatch run dev:atest
|
|
|
97
89
|
hatch run dev:utest
|
|
98
90
|
```
|
|
99
91
|
|
|
92
|
+
### Execute Tests Against the Version Matrix
|
|
93
|
+
|
|
94
|
+
Install Python 3.10 through 3.14 locally and make sure Hatch can find these interpreters. The test matrix covers Python 3.10-3.14 and Robot Framework 7.0 and 7.4.2.
|
|
95
|
+
|
|
96
|
+
Run all unit and acceptance tests against every matrix combination:
|
|
97
|
+
|
|
98
|
+
```shell
|
|
99
|
+
hatch run test:all
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
To test one combination only, for example Python 3.14 with Robot Framework 7.4.2:
|
|
103
|
+
|
|
104
|
+
```shell
|
|
105
|
+
hatch run test.py3.14-7.4.2:all
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Use `hatch env show` to list the generated matrix environments.
|
|
109
|
+
|
|
100
110
|
### Generate Docs via libdoc
|
|
101
111
|
|
|
102
112
|
```shell
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: robotframework-tablelibrary
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.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
|
|
@@ -16,6 +16,8 @@ Classifier: Programming Language :: Python
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.10
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
21
|
Requires-Python: >=3.10
|
|
20
22
|
Requires-Dist: openpyxl
|
|
21
23
|
Requires-Dist: overrides
|
|
@@ -110,6 +112,17 @@ ${result} = BuiltIn.Evaluate "index" not in "${content}"
|
|
|
110
112
|
BuiltIn.Should Be True ${result}
|
|
111
113
|
```
|
|
112
114
|
|
|
115
|
+
### Missing values
|
|
116
|
+
|
|
117
|
+
Missing values are returned using pandas' default values by default. To return parsed missing values as Python `None` in list, dictionary, cell, row, and column results, enable the setting. This includes pandas missing-value types such as `NaN`, `pd.NA`, and `NaT`, as well as values recognized as missing by the active parser, such as `N/A` and `NULL`:
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
Tables.Configure Missing As None True
|
|
121
|
+
${content} = Tables.Read Table ${CURDIR}${/}testdata${/}example_01.csv
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
DataFrame results and the library's internal DataFrames are not changed by this setting.
|
|
125
|
+
|
|
113
126
|
### File Format - Parquet
|
|
114
127
|
```
|
|
115
128
|
${content} = Tables.Read Table ${CURDIR}${/}testdata${/}example_05.parquet
|
|
@@ -73,6 +73,17 @@ ${result} = BuiltIn.Evaluate "index" not in "${content}"
|
|
|
73
73
|
BuiltIn.Should Be True ${result}
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
+
### Missing values
|
|
77
|
+
|
|
78
|
+
Missing values are returned using pandas' default values by default. To return parsed missing values as Python `None` in list, dictionary, cell, row, and column results, enable the setting. This includes pandas missing-value types such as `NaN`, `pd.NA`, and `NaT`, as well as values recognized as missing by the active parser, such as `N/A` and `NULL`:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
Tables.Configure Missing As None True
|
|
82
|
+
${content} = Tables.Read Table ${CURDIR}${/}testdata${/}example_01.csv
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
DataFrame results and the library's internal DataFrames are not changed by this setting.
|
|
86
|
+
|
|
76
87
|
### File Format - Parquet
|
|
77
88
|
```
|
|
78
89
|
${content} = Tables.Read Table ${CURDIR}${/}testdata${/}example_05.parquet
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
# Script takes one argument: e.g. 0.1.0 or 1.0.0
|
|
5
|
+
if [ -z "$1" ]; then
|
|
6
|
+
echo "❌ Usage: $0 <new_version>"
|
|
7
|
+
exit 1
|
|
8
|
+
fi
|
|
9
|
+
|
|
10
|
+
NEW_VERSION="$1"
|
|
11
|
+
TAG="v${NEW_VERSION}"
|
|
12
|
+
|
|
13
|
+
if git rev-parse "$TAG" >/dev/null 2>&1; then
|
|
14
|
+
echo "❌ Tag '${TAG}' already exists locally."
|
|
15
|
+
exit 1
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
if git ls-remote --exit-code --tags origin "refs/tags/${TAG}" >/dev/null 2>&1; then
|
|
19
|
+
echo "❌ Tag '${TAG}' already exists on origin."
|
|
20
|
+
exit 1
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
if ! git diff --quiet || ! git diff --cached --quiet; then
|
|
24
|
+
echo "❌ Working tree has uncommitted changes. Commit or stash them before creating a release."
|
|
25
|
+
exit 1
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
echo "🏷️ Creating '${TAG}' on the current commit"
|
|
29
|
+
git tag -a "$TAG" -m "Release ${TAG}"
|
|
30
|
+
git push origin "$TAG"
|
|
31
|
+
|
|
32
|
+
echo "✅ Tag '${TAG}' created and pushed. GitHub Actions will build and publish the release."
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["hatchling"]
|
|
2
|
+
requires = ["hatchling", "hatch-vcs"]
|
|
3
3
|
build-backend = "hatchling.build"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
@@ -21,6 +21,8 @@ classifiers = [
|
|
|
21
21
|
"Programming Language :: Python :: 3.10",
|
|
22
22
|
"Programming Language :: Python :: 3.11",
|
|
23
23
|
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Programming Language :: Python :: 3.14",
|
|
24
26
|
]
|
|
25
27
|
dependencies = [
|
|
26
28
|
"overrides",
|
|
@@ -58,7 +60,10 @@ only-include = ["src/Tables"]
|
|
|
58
60
|
sources = ["src"]
|
|
59
61
|
|
|
60
62
|
[tool.hatch.version]
|
|
61
|
-
|
|
63
|
+
source = "vcs"
|
|
64
|
+
|
|
65
|
+
[tool.hatch.build.hooks.vcs]
|
|
66
|
+
version-file = "src/Tables/_version.py"
|
|
62
67
|
|
|
63
68
|
[tool.hatch.build]
|
|
64
69
|
dev-mode-dirs = ["src"]
|
|
@@ -70,12 +75,28 @@ extra-dependencies = [
|
|
|
70
75
|
[tool.hatch.envs.types.scripts]
|
|
71
76
|
check = "mypy --install-types --non-interactive {args:src/Tables tests}"
|
|
72
77
|
|
|
78
|
+
[tool.hatch.envs.test]
|
|
79
|
+
dependencies = [
|
|
80
|
+
"pytest",
|
|
81
|
+
"pytest-cov",
|
|
82
|
+
"robotframework=={matrix:robotframework}",
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
[tool.hatch.envs.test.scripts]
|
|
86
|
+
atest = "robot tests/atest/"
|
|
87
|
+
utest = "pytest --cov=Tables --cov-report=term-missing --cov-branch --cov-fail-under=95"
|
|
88
|
+
all = ["utest", "atest"]
|
|
89
|
+
|
|
90
|
+
[[tool.hatch.envs.test.matrix]]
|
|
91
|
+
python = ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
92
|
+
robotframework = ["7.0", "7.4.2"]
|
|
93
|
+
|
|
73
94
|
[tool.coverage.run]
|
|
74
95
|
source_pkgs = ["Tables", "tests"]
|
|
75
96
|
branch = true
|
|
76
97
|
parallel = true
|
|
77
98
|
omit = [
|
|
78
|
-
"src/Tables/
|
|
99
|
+
"src/Tables/_version.py",
|
|
79
100
|
]
|
|
80
101
|
|
|
81
102
|
[tool.coverage.paths]
|
|
@@ -103,6 +124,8 @@ docs = "libdoc src/Tables results/keyword_docs.html"
|
|
|
103
124
|
|
|
104
125
|
[tool.mypy]
|
|
105
126
|
python_version = "3.10"
|
|
127
|
+
exclude = '(^|/)\.venv(/|$)'
|
|
128
|
+
follow_imports = "skip"
|
|
106
129
|
ignore_missing_imports = true
|
|
107
130
|
no_implicit_optional = true
|
|
108
131
|
strict_optional = true
|
{robotframework_tablelibrary-0.2.1 → robotframework_tablelibrary-0.3.1}/src/Tables/__init__.py
RENAMED
|
@@ -7,7 +7,7 @@ from robotlibcore import HybridCore
|
|
|
7
7
|
|
|
8
8
|
from Tables.utils.settings_stack import Scope, SettingsStack
|
|
9
9
|
|
|
10
|
-
from .
|
|
10
|
+
from ._version import __version__
|
|
11
11
|
from .keywords import (
|
|
12
12
|
Configuration,
|
|
13
13
|
Getter,
|
|
@@ -172,6 +172,7 @@ class Tables(HybridCore):
|
|
|
172
172
|
line_terminator: LineTerminator = LineTerminator.LF,
|
|
173
173
|
quoting: Quoting = Quoting.MINIMAL,
|
|
174
174
|
quoting_character: QuotingCharacter = QuotingCharacter['"'],
|
|
175
|
+
missing_as_none: bool = False,
|
|
175
176
|
):
|
|
176
177
|
"""
|
|
177
178
|
``TableLibrary`` can be controlled by the following arguments:
|
|
@@ -184,6 +185,7 @@ class Tables(HybridCore):
|
|
|
184
185
|
| ``line_terminator`` | Define the required line terminator for your table files. Default is ``False`` |
|
|
185
186
|
| ``quoting`` | Define which values should be surrounded with quotes, please check the CSV quoting for more details. Default is ``MINIMAL`` |
|
|
186
187
|
| ``quoting_character`` | Define quoting character to use for writing table files. Default is ``\"`` |
|
|
188
|
+
| ``missing_as_none`` | Return parsed missing values as Python ``None`` instead of pandas missing values. Default is ``False`` |
|
|
187
189
|
"""
|
|
188
190
|
|
|
189
191
|
# required variables for SettingsScope mechanism
|
|
@@ -202,6 +204,7 @@ class Tables(HybridCore):
|
|
|
202
204
|
)
|
|
203
205
|
self.scope_stack["quoting"] = SettingsStack(quoting, self)
|
|
204
206
|
self.scope_stack["quoting_character"] = SettingsStack(quoting_character, self)
|
|
207
|
+
self.scope_stack["missing_as_none"] = SettingsStack(missing_as_none, self)
|
|
205
208
|
self.scope_stack["enable_streaming"] = SettingsStack(False, self)
|
|
206
209
|
|
|
207
210
|
self.file_access = FileAccess(self)
|
|
@@ -222,12 +225,14 @@ class Tables(HybridCore):
|
|
|
222
225
|
super().__init__(libraries)
|
|
223
226
|
|
|
224
227
|
def _start_suite(self, _name: running.TestSuite, attrs: result.TestSuite):
|
|
225
|
-
|
|
226
|
-
self.
|
|
228
|
+
suite_id = attrs.id
|
|
229
|
+
self.suite_ids[suite_id] = None
|
|
230
|
+
self._add_to_scope_stack(suite_id, Scope.Suite)
|
|
227
231
|
|
|
228
232
|
def _start_test(self, _name: running.TestCase, attrs: result.TestCase):
|
|
229
|
-
|
|
230
|
-
self.
|
|
233
|
+
test_id = attrs.id
|
|
234
|
+
self.current_test_id = test_id
|
|
235
|
+
self._add_to_scope_stack(test_id, Scope.Test)
|
|
231
236
|
self.is_test_case_running = True
|
|
232
237
|
|
|
233
238
|
def _end_test(self, _name: running.TestCase, attrs: result.TestCase):
|
|
@@ -236,8 +241,9 @@ class Tables(HybridCore):
|
|
|
236
241
|
self.is_test_case_running = False
|
|
237
242
|
|
|
238
243
|
def _end_suite(self, _name: running.TestSuite, attrs: result.TestSuite):
|
|
239
|
-
|
|
240
|
-
self.
|
|
244
|
+
suite_id = attrs.id
|
|
245
|
+
self._remove_from_scope_stack(suite_id)
|
|
246
|
+
self.suite_ids.pop(suite_id, None)
|
|
241
247
|
|
|
242
248
|
def _add_to_scope_stack(self, scope_id: str, scope: Scope):
|
|
243
249
|
for stack in self.scope_stack.values():
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '0.3.1'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 3, 1)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = None
|
|
@@ -121,3 +121,16 @@ class LibraryAttributes:
|
|
|
121
121
|
@enable_streaming_stack.setter
|
|
122
122
|
def enable_streaming_stack(self, stack: SettingsStack):
|
|
123
123
|
self.library.scope_stack["enable_streaming"] = stack
|
|
124
|
+
|
|
125
|
+
# missing values
|
|
126
|
+
@property
|
|
127
|
+
def missing_as_none(self) -> bool:
|
|
128
|
+
return self.library.scope_stack["missing_as_none"].get()
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def missing_as_none_stack(self) -> SettingsStack:
|
|
132
|
+
return self.library.scope_stack["missing_as_none"]
|
|
133
|
+
|
|
134
|
+
@missing_as_none_stack.setter
|
|
135
|
+
def missing_as_none_stack(self, stack: SettingsStack):
|
|
136
|
+
self.library.scope_stack["missing_as_none"] = stack
|
|
@@ -25,6 +25,7 @@ class ConfigCtx:
|
|
|
25
25
|
file_encoding: FileEncoding
|
|
26
26
|
ignore_header: bool
|
|
27
27
|
enable_streaming: bool
|
|
28
|
+
missing_as_none: bool
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
class Configuration(LibraryAttributes):
|
|
@@ -45,6 +46,7 @@ class Configuration(LibraryAttributes):
|
|
|
45
46
|
file_encoding=self.file_encoding,
|
|
46
47
|
ignore_header=self.ignore_header,
|
|
47
48
|
enable_streaming=self.enable_streaming,
|
|
49
|
+
missing_as_none=self.missing_as_none,
|
|
48
50
|
)
|
|
49
51
|
)
|
|
50
52
|
|
|
@@ -199,3 +201,26 @@ class Configuration(LibraryAttributes):
|
|
|
199
201
|
self.enable_streaming_stack.set(enable_streaming, scope)
|
|
200
202
|
logger.debug(f"'file_type': old value: {old_config} - new value: {enable_streaming}")
|
|
201
203
|
return old_config
|
|
204
|
+
|
|
205
|
+
@keyword(tags=["Configuration"])
|
|
206
|
+
def configure_missing_as_none(self, missing_as_none: bool, scope: Scope = Scope.Suite):
|
|
207
|
+
"""
|
|
208
|
+
Configure whether parsed missing values are returned as Python ``None``.
|
|
209
|
+
|
|
210
|
+
When enabled, pandas missing values such as ``NaN``, ``pd.NA`` and ``NaT``
|
|
211
|
+
are converted to ``None`` in list, dictionary, cell, row and column results.
|
|
212
|
+
Values recognized as missing by the active parser, such as ``N/A`` and ``NULL``,
|
|
213
|
+
are included. DataFrame results and the library's internal DataFrames are not changed.
|
|
214
|
+
|
|
215
|
+
| =`Arguments`= | =`Description`= |
|
|
216
|
+
| ``missing_as_none`` | Set to ``True`` to return missing values as Python ``None``. Default: ``False`` |
|
|
217
|
+
| ``scope`` | Scope in which the setting applies. |
|
|
218
|
+
|
|
219
|
+
== Example ==
|
|
220
|
+
| Configure Missing As None True
|
|
221
|
+
| Configure Missing As None False
|
|
222
|
+
"""
|
|
223
|
+
old_config = self.missing_as_none
|
|
224
|
+
self.missing_as_none_stack.set(missing_as_none, scope)
|
|
225
|
+
logger.debug(f"'missing_as_none': old value: {old_config} - new value: {missing_as_none}")
|
|
226
|
+
return old_config
|
|
@@ -58,6 +58,8 @@ class Getter(LibraryAttributes):
|
|
|
58
58
|
table_df = table_df.iloc[1:]
|
|
59
59
|
data = data[1:]
|
|
60
60
|
|
|
61
|
+
data = self.file_reader.convert_missing_values(data)
|
|
62
|
+
|
|
61
63
|
if return_type == TableFormat["List of dicts"]:
|
|
62
64
|
df_for_dicts = table_df
|
|
63
65
|
|
|
@@ -65,7 +67,9 @@ class Getter(LibraryAttributes):
|
|
|
65
67
|
header = [str(x) for x in df_for_dicts.iloc[0].tolist()]
|
|
66
68
|
df_for_dicts = df_for_dicts.iloc[1:].copy()
|
|
67
69
|
df_for_dicts.columns = header
|
|
68
|
-
return
|
|
70
|
+
return self.file_reader.convert_missing_values(
|
|
71
|
+
cast(list[dict[str, Any]], df_for_dicts.to_dict(orient="records"))
|
|
72
|
+
)
|
|
69
73
|
return data
|
|
70
74
|
|
|
71
75
|
@keyword(tags=["Getter"])
|
|
@@ -196,6 +200,9 @@ class Getter(LibraryAttributes):
|
|
|
196
200
|
| =`Arguments`= | =`Description`= |
|
|
197
201
|
| ``return_type`` | Choose what type of table format it should return. Default: list of lists|
|
|
198
202
|
|
|
203
|
+
If ``Configure Missing As None`` is enabled, missing values are returned as
|
|
204
|
+
Python ``None`` for list and dictionary results. DataFrame results are unchanged.
|
|
205
|
+
|
|
199
206
|
== Return Value ==
|
|
200
207
|
Return table in form as either list of lists, list of dicts or dataframe.
|
|
201
208
|
|
|
@@ -229,6 +236,9 @@ class Getter(LibraryAttributes):
|
|
|
229
236
|
| ``assertion_expected`` | See ``robotframework-assertion-engine`` for more details |
|
|
230
237
|
| ``message`` | Custom error message for failed assertion |
|
|
231
238
|
|
|
239
|
+
If ``Configure Missing As None`` is enabled, a missing cell is returned as
|
|
240
|
+
Python ``None`` and assertions are evaluated against the converted value.
|
|
241
|
+
|
|
232
242
|
== Return Value ==
|
|
233
243
|
Keyword returns the value of the given cell.
|
|
234
244
|
In case of a failed assertion, the keyword will just fail without returning anything.
|
|
@@ -245,6 +255,7 @@ class Getter(LibraryAttributes):
|
|
|
245
255
|
column = self.file_reader.cast_column_type(column)
|
|
246
256
|
|
|
247
257
|
cell = table_df.loc[row, column] if isinstance(column, str) else table_df.iloc[row, column]
|
|
258
|
+
cell = self.file_reader.convert_missing_values(cell)
|
|
248
259
|
|
|
249
260
|
if assertion_expected:
|
|
250
261
|
if assertion_operator not in NumericalOperators:
|
|
@@ -272,6 +283,9 @@ class Getter(LibraryAttributes):
|
|
|
272
283
|
| ``assertion_expected`` | See ``robotframework-assertion-engine`` for more details |
|
|
273
284
|
| ``message`` | Custom error message for failed assertion |
|
|
274
285
|
|
|
286
|
+
If ``Configure Missing As None`` is enabled, missing column values are returned
|
|
287
|
+
as Python ``None`` and assertions are evaluated against the converted values.
|
|
288
|
+
|
|
275
289
|
== Return Value ==
|
|
276
290
|
Returns column values as a list.
|
|
277
291
|
|
|
@@ -291,7 +305,7 @@ class Getter(LibraryAttributes):
|
|
|
291
305
|
column = self.file_reader.cast_column_type(column)
|
|
292
306
|
|
|
293
307
|
column_df = table_df.loc[:, column] if isinstance(column, str) else table_df.iloc[:, column]
|
|
294
|
-
column_list = cast(list[Any], column_df.to_list())
|
|
308
|
+
column_list = self.file_reader.convert_missing_values(cast(list[Any], column_df.to_list()))
|
|
295
309
|
|
|
296
310
|
if assertion_expected:
|
|
297
311
|
if assertion_operator not in valid_assertions:
|
|
@@ -318,6 +332,9 @@ class Getter(LibraryAttributes):
|
|
|
318
332
|
| ``assertion_expected`` | See ``robotframework-assertion-engine`` for more details |
|
|
319
333
|
| ``message`` | Custom error message for failed assertion |
|
|
320
334
|
|
|
335
|
+
If ``Configure Missing As None`` is enabled, missing row values are returned as
|
|
336
|
+
Python ``None`` and assertions are evaluated against the converted values.
|
|
337
|
+
|
|
321
338
|
== Return Value ==
|
|
322
339
|
Returns row values as a list.
|
|
323
340
|
|
|
@@ -335,7 +352,7 @@ class Getter(LibraryAttributes):
|
|
|
335
352
|
current_df = self.file_reader.file_sync.table_storage[self.file_reader.file_sync.current_file].data
|
|
336
353
|
table_df = self.file_reader.validate_table_to_dataframe(data=current_df, row=row)
|
|
337
354
|
|
|
338
|
-
row_list = cast(list[Any], table_df.iloc[row].to_list())
|
|
355
|
+
row_list = self.file_reader.convert_missing_values(cast(list[Any], table_df.iloc[row].to_list()))
|
|
339
356
|
|
|
340
357
|
if assertion_expected:
|
|
341
358
|
if assertion_operator not in valid_assertions:
|
|
@@ -347,7 +364,7 @@ class Getter(LibraryAttributes):
|
|
|
347
364
|
return row_list
|
|
348
365
|
|
|
349
366
|
@keyword(tags=["Getter"])
|
|
350
|
-
def count_table( # noqa
|
|
367
|
+
def count_table( # noqa PLR0913
|
|
351
368
|
self,
|
|
352
369
|
path: Path | str,
|
|
353
370
|
axis: Axis,
|
|
@@ -50,7 +50,7 @@ class FileReader(LibraryAttributes):
|
|
|
50
50
|
if self.file_type == FileType.Parquet and not self.ignore_header:
|
|
51
51
|
list_data.insert(0, list(data.columns))
|
|
52
52
|
|
|
53
|
-
return list_data
|
|
53
|
+
return self.convert_missing_values(list_data)
|
|
54
54
|
if return_type == TableFormat["List of dicts"]:
|
|
55
55
|
df_for_dicts = data
|
|
56
56
|
|
|
@@ -58,11 +58,25 @@ class FileReader(LibraryAttributes):
|
|
|
58
58
|
header = [str(x) for x in df_for_dicts.iloc[0].tolist()]
|
|
59
59
|
df_for_dicts = df_for_dicts.iloc[1:].copy()
|
|
60
60
|
df_for_dicts.columns = header
|
|
61
|
-
return cast(list[dict[str, Any]], df_for_dicts.to_dict(orient="records"))
|
|
61
|
+
return self.convert_missing_values(cast(list[dict[str, Any]], df_for_dicts.to_dict(orient="records")))
|
|
62
62
|
if return_type == TableFormat["Dataframe"]:
|
|
63
63
|
return data
|
|
64
64
|
raise ValueError(f"Invalid TableFormat type. Please select valid values: {[key.name for key in TableFormat]}")
|
|
65
65
|
|
|
66
|
+
def convert_missing_values(self, value: Any) -> Any:
|
|
67
|
+
if not self.missing_as_none:
|
|
68
|
+
return value
|
|
69
|
+
if isinstance(value, list):
|
|
70
|
+
return [self.convert_missing_values(item) for item in value]
|
|
71
|
+
if isinstance(value, dict):
|
|
72
|
+
return {key: self.convert_missing_values(item) for key, item in value.items()}
|
|
73
|
+
try:
|
|
74
|
+
if bool(pd.isna(value)):
|
|
75
|
+
return None
|
|
76
|
+
except (TypeError, ValueError):
|
|
77
|
+
pass
|
|
78
|
+
return value
|
|
79
|
+
|
|
66
80
|
def file_exists(self, path: Path) -> bool | FileNotFoundError:
|
|
67
81
|
if not path.is_file():
|
|
68
82
|
raise FileNotFoundError(f"File not found: {path}")
|
|
@@ -206,8 +220,8 @@ class FileReader(LibraryAttributes):
|
|
|
206
220
|
def validate_table_to_dataframe(
|
|
207
221
|
self,
|
|
208
222
|
data: list[list] | DataFrame,
|
|
209
|
-
row:
|
|
210
|
-
column:
|
|
223
|
+
row: int | None = None,
|
|
224
|
+
column: str | int | None = None,
|
|
211
225
|
) -> DataFrame:
|
|
212
226
|
"""Formats a table (list of lists or dataframe) to dataframe. Also checks
|
|
213
227
|
if provided row or column are valid (see validate_row/ validate_column)."""
|
|
@@ -219,8 +219,8 @@ class FileWriter(LibraryAttributes):
|
|
|
219
219
|
def set_dataframe_cells(
|
|
220
220
|
self,
|
|
221
221
|
data: Any,
|
|
222
|
-
row:
|
|
223
|
-
column:
|
|
222
|
+
row: int | None = None,
|
|
223
|
+
column: str | int | None = None,
|
|
224
224
|
header: bool = True,
|
|
225
225
|
return_type: TableFormat = TableFormat["Dataframe"],
|
|
226
226
|
) -> list[list] | list[dict] | DataFrame:
|