Skip to content

Commit e64553b

Browse files
authored
Addressing some issues in the Python and Rust wrappers. (#164)
**New Features** - The Python wrapper now packages the tzdata database inside the wheel to ensure consistent performance across platforms. - The Rust wrapper now has the ability to download a fresh copy of the tzdata database if needed - Added a `regoSetTZDataPath` method to the C API and exposed it for the Python and Rust wrappers. - The `regoNew` C API method now supports the `v1_compatible` flag for interpreter creation - The library embeds the `windowsZones.xml` mapping file so it can provide it where needed - The Python wrapper provides a more natural interface for sets and objects - The CMake system will now look for a `REGOCPP_TZDATA_PATH` environment variable to use for setting the default path **Bug Fix** - Fixed a bug where builtins would not be available if an interpreter was re-used - Fixed a bug with the Rust wrapper where it was aggressively trimming strings Signed-off-by: Matthew Johnson <[email protected]>
1 parent 3eb9a7c commit e64553b

27 files changed

+424
-58
lines changed

.github/workflows/build_wheels.yml

+9-10
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ jobs:
2828
with:
2929
package-dir: ${{github.workspace}}/wrappers/python/
3030

31-
- uses: actions/upload-artifact@v3
31+
- uses: actions/upload-artifact@v4
3232
with:
33+
name: ${{ matrix.python }}-windows
3334
path: ./wheelhouse/*.whl
3435
build_mac_wheels:
3536
name: Build wheels on mac
@@ -47,11 +48,6 @@ jobs:
4748
python-version: "3.10 - 3.12"
4849
update-environment: false
4950

50-
- name: Install pipx
51-
run: |
52-
brew install pipx
53-
pipx ensurepath
54-
5551
- name: Git config for fetching pull requests
5652
run: |
5753
git config --global --add remote.origin.fetch +refs/pull/*/merge:refs/remotes/pull/*
@@ -66,14 +62,15 @@ jobs:
6662
CIBW_BUILD: ${{ matrix.python }}-macosx*
6763
CIBW_ARCHS_MACOS: arm64
6864
CIBW_ENVIRONMENT_MACOS : >
69-
MACOSX_DEPLOYMENT_TARGET=10.15
65+
MACOSX_DEPLOYMENT_TARGET=11.0
7066
REGOCPP_REPO=https://github.com/${{github.repository}}
7167
REGOCPP_TAG=${{github.sha}}
7268
with:
7369
package-dir: ${{github.workspace}}/wrappers/python/
7470

75-
- uses: actions/upload-artifact@v3
71+
- uses: actions/upload-artifact@v4
7672
with:
73+
name: ${{ matrix.python }}-mac
7774
path: ./wheelhouse/*.whl
7875
build_manylinux_wheels:
7976
name: Build wheels on manylinux
@@ -100,8 +97,9 @@ jobs:
10097
with:
10198
package-dir: ${{github.workspace}}/wrappers/python/
10299

103-
- uses: actions/upload-artifact@v3
100+
- uses: actions/upload-artifact@v4
104101
with:
102+
name: ${{ matrix.python }}-manylinux
105103
path: ./wheelhouse/*.whl
106104

107105
build_musllinux_wheels:
@@ -128,6 +126,7 @@ jobs:
128126
with:
129127
package-dir: ${{github.workspace}}/wrappers/python/
130128

131-
- uses: actions/upload-artifact@v3
129+
- uses: actions/upload-artifact@v4
132130
with:
131+
name: ${{ matrix.python }}-musllinux
133132
path: ./wheelhouse/*.whl

CHANGELOG

+17
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
11
# Changelog
22

3+
## 2024-09-26 - Version 0.4.5
4+
Point release addressing some issues in the Python and Rust wrappers.
5+
6+
**New Features**
7+
- The Python wrapper now packages the tzdata database inside the wheel to ensure consistent performance across platforms.
8+
- The Rust wrapper now has the ability to download a fresh copy of the tzdata database if needed
9+
- Added a `regoSetTZDataPath` method to the C API and exposed it for the Python and Rust wrappers.
10+
- The `regoNew` C API method now supports the `v1_compatible` flag for interpreter creation
11+
- The library embeds the `windowsZones.xml` mapping file so it can provide it where needed
12+
- The Python wrapper provides a more natural interface for sets and objects
13+
- The CMake system will now look for a `REGOCPP_TZDATA_PATH` environment variable to use for setting the default path
14+
15+
**Bug Fix**
16+
- Fixed a bug where builtins would not be available if an interpreter was re-used
17+
- Fixed a bug with the Rust wrapper where it was aggressively trimming strings
18+
19+
320
## 2024-09-23 - Version 0.4.4
421
Point release adding the `uuid`, `time`, and `walk` builtins.
522

CMakeLists.txt

+25-3
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,18 @@ if(MSVC)
110110
set(REGOCPP_USE_MANUAL_TZDATA ON)
111111
endif()
112112

113-
set(REGOCPP_TZDATA_PATH "${CMAKE_INSTALL_PREFIX}" CACHE STRING "Path to the tzdata directory")
113+
if(DEFINED ENV{REGOCPP_TZDATA_PATH})
114+
set(REGOCPP_TZDATA_PATH $ENV{REGOCPP_TZDATA_PATH} CACHE STRING "Path to the tzdata directory")
115+
message("REGOCPP_TZDATA_PATH set to ${REGOCPP_TZDATA_PATH}")
116+
else()
117+
set(REGOCPP_TZDATA_PATH "${CMAKE_INSTALL_PREFIX}" CACHE STRING "Path to the tzdata directory")
118+
message("REGOCPP_TZDATA_PATH not set, using ${REGOCPP_TZDATA_PATH}")
119+
endif()
120+
114121
file(TO_CMAKE_PATH ${REGOCPP_TZDATA_PATH} REGOCPP_TZDATA_PATH)
115122

123+
set(REGOCPP_WINDOWS_ZONES_PATH "${CMAKE_CURRENT_SOURCE_DIR}/templates/tzdata/windowsZones.xml")
124+
116125
if(REGOCPP_USE_MANUAL_TZDATA)
117126
set(MANUAL_TZ_DB ON)
118127
set(USE_SYSTEM_TZ_DB OFF)
@@ -124,13 +133,18 @@ if(REGOCPP_USE_MANUAL_TZDATA)
124133
FILE(DOWNLOAD https://www.iana.org/time-zones/repository/tzdata-latest.tar.gz ${TZDATA_GZ_PATH} SHOW_PROGRESS)
125134
message("Extracting ${TZDATA_GZ_PATH} to ${TZDATA_PATH}")
126135
FILE(ARCHIVE_EXTRACT INPUT ${TZDATA_GZ_PATH} DESTINATION ${TZDATA_PATH})
136+
endif()
127137

138+
if(NOT EXISTS ${REGOCPP_TZDATA_PATH}/tzdata)
128139
# Copy the tzdata directory to the specified path.
129140
message("Copying ${TZDATA_PATH} to ${REGOCPP_TZDATA_PATH}")
130141
file(COPY ${TZDATA_PATH} DESTINATION ${REGOCPP_TZDATA_PATH})
131-
if(MSVC)
142+
endif()
143+
144+
if(MSVC)
145+
if(NOT EXISTS ${REGOCPP_TZDATA_PATH}/tzdata/windowsZones.xml)
132146
message("MSVC: Copying windowsZones.xml to ${REGOCPP_TZDATA_PATH}/tzdata")
133-
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/templates/tzdata/windowsZones.xml DESTINATION ${REGOCPP_TZDATA_PATH}/tzdata)
147+
file(COPY ${REGOCPP_WINDOWS_ZONES_PATH} DESTINATION ${REGOCPP_TZDATA_PATH}/tzdata)
134148
endif()
135149
endif()
136150
else()
@@ -139,6 +153,14 @@ else()
139153
set(MANUAL_TZ_DB OFF)
140154
endif()
141155

156+
file( READ ${REGOCPP_WINDOWS_ZONES_PATH} REGOCPP_WINDOWS_ZONES_RAW )
157+
string(LENGTH "${REGOCPP_WINDOWS_ZONES_RAW}" REGOCPP_WINDOWS_ZONES_RAW_LENGTH)
158+
math(EXPR REGOCPP_WINDOWS_ZONES_RAW_LENGTH "${REGOCPP_WINDOWS_ZONES_RAW_LENGTH} - 1")
159+
foreach(iter RANGE 0 ${REGOCPP_WINDOWS_ZONES_RAW_LENGTH} 2048)
160+
string(SUBSTRING "${REGOCPP_WINDOWS_ZONES_RAW}" ${iter} 2048 line)
161+
string(APPEND REGOCPP_WINDOWS_ZONES_SRC "${line})xml\",\n\tR\"xml(")
162+
endforeach()
163+
142164
set(BUILD_TZ_LIB ON)
143165
set(COMPILE_WITH_C_LOCALE ON)
144166
FetchContent_MakeAvailable_ExcludeFromAll(date)

VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.4.4
1+
0.4.5

examples/rust/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ edition = "2021"
66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
77

88
[dependencies]
9-
regorust = "0.4.4"
9+
regorust = "0.4.5"
1010
clap = { version = "4.0", features = ["derive"] }

include/rego/rego.hh

+13
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,19 @@ namespace rego
560560
*/
561561
Node set(const Nodes& set_members);
562562

563+
/**
564+
* Sets the location where rego-cpp will look for timezone database
565+
* information.
566+
*
567+
* The timezone database will be interpreted as one obtained from the IANA
568+
* (https://www.iana.org/time-zones) which has been downloaded and unpacked
569+
* into at the path provided. If the library was built without manual tzdata
570+
* support, this function will throw an exception.
571+
*
572+
* @param path The path to the timezone database.
573+
*/
574+
void set_tzdata_path(const std::filesystem::path& path);
575+
563576
/** This constant indicates that a built-in can receive any number of
564577
* arguments. */
565578
const std::size_t AnyArity = std::numeric_limits<std::size_t>::max();

include/rego/rego_c.h

+25
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ typedef unsigned int regoSize;
2020
#define REGO_ERROR 1
2121
#define REGO_ERROR_BUFFER_TOO_SMALL 2
2222
#define REGO_ERROR_INVALID_LOG_LEVEL 3
23+
#define REGO_ERROR_MANUAL_TZDATA_NOT_SUPPORTED 4
2324

2425
// term node types
2526
#define REGO_NODE_BINDING 1000
@@ -93,6 +94,21 @@ extern "C"
9394
*/
9495
regoEnum regoSetLogLevelFromString(const char* level);
9596

97+
/**
98+
* Sets the location where rego-cpp will look for timezone database
99+
* information.
100+
*
101+
* The timezone database will be interpreted as one obtained from the IANA
102+
* (https://www.iana.org/time-zones) which has been downloaded and unpacked
103+
* into at the path provided. If the library was built without manual tzdata
104+
* support, this function will return an error code.
105+
*
106+
* @param path The path to the timezone database.
107+
* @return REGO_OK if successful, REGO_ERROR_MANUAL_TZDATA_NOT_SUPPORTED
108+
* otherwise.
109+
*/
110+
regoEnum regoSetTZDataPath(const char* path);
111+
96112
/**
97113
* Allocates and initializes a new Rego interpreter.
98114
*
@@ -102,6 +118,15 @@ extern "C"
102118
*/
103119
regoInterpreter* regoNew(void);
104120

121+
/**
122+
* Allocates and initializes a new V1 Rego interpreter.
123+
*
124+
* The caller is responsible for freeing the interpreter with regoFree.
125+
*
126+
* @return A pointer to the new V1 interpreter.
127+
*/
128+
regoInterpreter* regoNewV1(void);
129+
105130
/**
106131
* Frees a Rego interpreter.
107132
*

src/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/version.h.in" "${CMAKE_CURRENT_BINARY_DIR}/version.h" @ONLY)
22
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/builtins/tzdata.h.in" "${CMAKE_CURRENT_BINARY_DIR}/tzdata.h" @ONLY)
3+
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/windows_zones.h.in" "${CMAKE_CURRENT_BINARY_DIR}/windows_zones.h" @ONLY)
34

45
set( SOURCES
56
bigint.cc

src/builtins/time.cc

+31
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
#include "re2/stringpiece.h"
55
#include "rego.hh"
66
#include "tzdata.h"
7+
#include "windows_zones.h"
78

89
#include <chrono>
10+
#include <fstream>
911
#include <inttypes.h>
1012

1113
namespace
@@ -739,4 +741,33 @@ namespace rego
739741
BuiltInDef::create(Location("time.weekday"), 1, ::weekday)};
740742
}
741743
}
744+
745+
void set_tzdata_path(const std::filesystem::path& path)
746+
{
747+
#ifdef REGOCPP_USE_MANUAL_TZDATA
748+
date::set_install(path.string());
749+
auto wz_path = path / "windowsZones.xml";
750+
if (!std::filesystem::exists(wz_path))
751+
{
752+
std::ofstream file(wz_path);
753+
if (file.is_open())
754+
{
755+
for (auto& line : WINDOWS_ZONES_SRC)
756+
{
757+
file << line << std::endl;
758+
}
759+
file.close();
760+
}
761+
else
762+
{
763+
throw std::runtime_error(
764+
"Failed to create required windowsZones.xml at tzdata path");
765+
}
766+
}
767+
#else
768+
throw std::runtime_error(
769+
"Cannot set tzdata path to " + path.string() +
770+
" because REGOCPP_USE_MANUAL_TZDATA was not defined");
771+
#endif
772+
}
742773
}

src/rego_c.cc

+21-1
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,33 @@ extern "C"
7575
return REGO_ERROR_INVALID_LOG_LEVEL;
7676
}
7777

78+
regoEnum regoSetTZDataPath(const char* path)
79+
{
80+
try
81+
{
82+
rego::set_tzdata_path(path);
83+
return REGO_OK;
84+
}
85+
catch (const std::exception&)
86+
{
87+
return REGO_ERROR_MANUAL_TZDATA_NOT_SUPPORTED;
88+
}
89+
}
90+
7891
regoInterpreter* regoNew()
7992
{
80-
auto ptr = reinterpret_cast<regoInterpreter*>(new rego::Interpreter());
93+
auto ptr = reinterpret_cast<regoInterpreter*>(new rego::Interpreter(false));
8194
logging::Debug() << "regoNew: " << ptr;
8295
return ptr;
8396
}
8497

98+
regoInterpreter* regoNewV1()
99+
{
100+
auto ptr = reinterpret_cast<regoInterpreter*>(new rego::Interpreter(true));
101+
logging::Debug() << "regoNewV1: " << ptr;
102+
return ptr;
103+
}
104+
85105
void regoFree(regoInterpreter* rego)
86106
{
87107
logging::Debug() << "regoFree: " << rego;

src/unify/skip_refs.cc

+2-3
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,10 @@ namespace rego
165165
return 0;
166166
});
167167

168-
auto added_builtins = std::make_shared<std::set<std::string>>();
169-
170-
skip_refs.post(Rego, [skips, builtins, added_builtins](Node node) {
168+
skip_refs.post(Rego, [skips, builtins](Node node) {
171169
std::set<std::string> used_builtins;
172170
find_used_builtins(node, builtins, used_builtins);
171+
auto added_builtins = std::make_shared<std::set<std::string>>();
173172
Node skipseq = node / SkipSeq;
174173
int changes = 0;
175174
for (auto& builtin : used_builtins)

src/windows_zones.h.in

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
#ifndef _REGOCPP_WINDOWS_ZONES_H_
5+
#define _REGOCPP_WINDOWS_ZONES_H_
6+
7+
#include <string>
8+
#include <vector>
9+
10+
namespace rego
11+
{
12+
const std::vector<std::string> WINDOWS_ZONES_SRC = {
13+
R"xml(@REGOCPP_WINDOWS_ZONES_SRC@)xml"
14+
};
15+
} // namespace rego
16+
17+
#endif

wrappers/python/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,5 @@ dmypy.json
127127

128128
# Pyre type checker
129129
.pyre/
130+
tzdata
131+
debug

wrappers/python/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ FetchContent_Declare(
3636
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
3737
set(SNMALLOC_ENABLE_DYNAMIC_LOADING ON)
3838
set(REGOCPP_USE_CXX17 ON)
39+
set(REGOCPP_USE_MANUAL_TZDATA ON)
3940

4041
FetchContent_MakeAvailable(regocpp)
4142
FetchContent_MakeAvailable(pybind11)

wrappers/python/MANIFEST.in

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
graft src

wrappers/python/docs/source/conf.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
project = 'regopy'
1010
copyright = '2024, Microsoft'
1111
author = 'Microsoft'
12-
release = '0.4.4'
12+
release = '0.4.5'
1313

1414
# -- General configuration ---------------------------------------------------
1515
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

wrappers/python/setup.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ def build_extension(self, ext: CMakeExtension):
6565
cfg = "Debug" if self.debug else "Release"
6666
extdir = os.path.abspath(os.path.dirname(
6767
self.get_ext_fullpath(ext.name)))
68+
6869
cmake_args = ["-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=" + extdir,
6970
"-DCMAKE_BUILD_TYPE=" + cfg,
7071
"-DPYTHON_EXECUTABLE=" + sys.executable]
@@ -83,8 +84,11 @@ def build_extension(self, ext: CMakeExtension):
8384
else:
8485
cmake_args += ["-A", "Win32"]
8586

87+
env = os.environ.copy()
88+
env["REGOCPP_TZDATA_PATH"] = extdir
89+
8690
subprocess.check_call(["cmake", ext.source_dir] +
87-
cmake_args, cwd=self.build_temp)
91+
cmake_args, cwd=self.build_temp, env=env)
8892
subprocess.check_call(["cmake", "--build", "."] +
8993
build_args, cwd=self.build_temp)
9094

@@ -100,6 +104,7 @@ def build_extension(self, ext: CMakeExtension):
100104
long_description_content_type="text/markdown",
101105
packages=find_packages("src"),
102106
package_dir={"": "src"},
107+
include_package_data=True,
103108
python_requires=">=3.6, <4",
104109
ext_modules=[CMakeExtension("regopy._regopy")],
105110
classifiers=[

0 commit comments

Comments
 (0)