diff --git a/.github/workflows/bazel.yml b/.github/workflows/bazel.yml index f9ee8580e..537167599 100644 --- a/.github/workflows/bazel.yml +++ b/.github/workflows/bazel.yml @@ -16,5 +16,9 @@ jobs: steps: - uses: actions/checkout@v4 - uses: bazelbuild/setup-bazelisk@v3 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" - run: bazel build //:opencc - - run: bazel test --test_output=all //src/... //data/... //test/... + - run: bazel test --test_output=all //src/... //data/... //test/... //python/... diff --git a/BUILD.bazel b/BUILD.bazel index 6c61b49ba..263edc9b9 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -1,3 +1,6 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") +load("@rules_python//python:py_library.bzl", "py_library") + package(default_visibility = ["//visibility:public"]) cc_library( @@ -17,3 +20,10 @@ cc_library( "//src:opencc", ], ) + +py_library( + name = "py_opencc", + deps = [ + "//python/opencc", + ], +) diff --git a/MODULE.bazel b/MODULE.bazel index 743d74efe..b59448e9d 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -9,7 +9,22 @@ module( bazel_dep(name = "darts-clone", version = "0.32") bazel_dep(name = "googletest", version = "1.15.0", dev_dependency = True) bazel_dep(name = "marisa-trie", version = "0.2.6") +bazel_dep(name = "platforms", version = "0.0.10") +bazel_dep(name = "pybind11_bazel", version = "2.12.0") bazel_dep(name = "rapidjson", version = "1.1.0") bazel_dep(name = "rules_cc", version = "0.0.9") bazel_dep(name = "rules_python", version = "0.34.0") bazel_dep(name = "tclap", version = "1.2.5") + + +python = use_extension("@rules_python//python/extensions:python.bzl", "python") +python.toolchain( + python_version = "3.12", +) +pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip") +pip.parse( + hub_name = "pip", + python_version = "3.12", + requirements_lock = "//python/tests:requirements_lock.txt", +) +use_repo(pip, "pip") diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index cbb5f6a0f..e794d2d28 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -152,6 +152,37 @@ }, "recordedRepoMappingEntries": [] } + }, + "@@pybind11_bazel~//:internal_configure.bzl%internal_configure_extension": { + "general": { + "bzlTransitiveDigest": "+F47SE20NlARCHVGbd4r7kkjg4OA0eCJcOd5fqKq4fQ=", + "usagesDigest": "iH2lKTfsNEpn2MqtGpBNwJrxbb2C7DiYmh/XuKgDtr8=", + "recordedFileInputs": { + "@@pybind11_bazel~//MODULE.bazel": "e6f4c20442eaa7c90d7190d8dc539d0ab422f95c65a57cc59562170c58ae3d34" + }, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "pybind11": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "build_file": "@@pybind11_bazel~//:pybind11-BUILD.bazel", + "strip_prefix": "pybind11-2.12.0", + "urls": [ + "https://github.com/pybind/pybind11/archive/v2.12.0.zip" + ] + } + } + }, + "recordedRepoMappingEntries": [ + [ + "pybind11_bazel~", + "bazel_tools", + "bazel_tools" + ] + ] + } } } } diff --git a/python/opencc/BUILD.bazel b/python/opencc/BUILD.bazel new file mode 100644 index 000000000..876b1ca34 --- /dev/null +++ b/python/opencc/BUILD.bazel @@ -0,0 +1,15 @@ +load("@rules_python//python:py_library.bzl", "py_library") + +package(default_visibility = ["//visibility:public"]) + +py_library( + name = "opencc", + srcs = ["__init__.py"], + data = [ + "//data/config", + "//data/dictionary:binary_dictionaries", + "//data/dictionary:text_dictionaries", + ], + imports = [".."], + deps = ["//src:py_opencc"], +) diff --git a/python/opencc/__init__.py b/python/opencc/__init__.py index f77f80cf9..3884bdc7f 100644 --- a/python/opencc/__init__.py +++ b/python/opencc/__init__.py @@ -3,13 +3,18 @@ import os import sys -from opencc.clib import opencc_clib +try: + import opencc_clib +except ImportError: + from opencc.clib import opencc_clib __all__ = ['OpenCC', 'CONFIGS', '__version__'] __version__ = opencc_clib.__version__ -_thisdir = os.path.dirname(os.path.abspath(__file__)) -_opencc_share_dir = os.path.join(_thisdir, 'clib', 'share', 'opencc') +_this_dir = os.path.dirname(os.path.abspath(__file__)) +_opencc_share_dir = os.path.join(_this_dir, 'clib', 'share', 'opencc') +_opencc_rootdir = os.path.abspath(os.path.join(_this_dir, '..', '..')) +_opencc_configdir = os.path.join(_opencc_rootdir, 'data', 'config') if sys.version_info.major == 2: text_type = unicode # noqa @@ -18,6 +23,8 @@ if os.path.isdir(_opencc_share_dir): CONFIGS = [f for f in os.listdir(_opencc_share_dir) if f.endswith('.json')] +elif os.path.isdir(_opencc_configdir): + CONFIGS = [f for f in os.listdir(_opencc_configdir) if f.endswith('.json')] else: CONFIGS = [] @@ -39,7 +46,9 @@ def __init__(self, config='t2s'): if not config.endswith('.json'): config += '.json' if not os.path.isfile(config): - config = os.path.join(_opencc_share_dir, config) + config_under_share_dir = os.path.join(_opencc_share_dir, config) + if os.path.isfile(config_under_share_dir): + config = config_under_share_dir super(OpenCC, self).__init__(config) self.config = config diff --git a/python/tests/BUILD.bazel b/python/tests/BUILD.bazel new file mode 100644 index 000000000..a8ca16674 --- /dev/null +++ b/python/tests/BUILD.bazel @@ -0,0 +1,16 @@ +load("@pip//:requirements.bzl", "requirement") +load("@rules_python//python:py_test.bzl", "py_test") + +py_test( + name = "test_opencc", + srcs = ["test_opencc.py"], + data = [ + "//test/testcases", + ], + imports = [".."], + deps = [ + "//python/opencc", + requirement("pytest"), + requirement("exceptiongroup"), + ], +) diff --git a/python/tests/requirements_lock.txt b/python/tests/requirements_lock.txt new file mode 100644 index 000000000..330e2743e --- /dev/null +++ b/python/tests/requirements_lock.txt @@ -0,0 +1,5 @@ +exceptiongroup==1.2.2 +iniconfig==2.0.0 +packaging==24.1 +pluggy==1.5.0 +pytest==8.3.2 diff --git a/python/tests/test_opencc.py b/python/tests/test_opencc.py index 8ca83b7ef..31b2b0453 100644 --- a/python/tests/test_opencc.py +++ b/python/tests/test_opencc.py @@ -1,6 +1,9 @@ from __future__ import unicode_literals import os +import pytest +import sys + from glob import glob _this_dir = os.path.dirname(os.path.abspath(__file__)) @@ -39,3 +42,7 @@ def test_conversion(): for text, ans in zip(intexts, anstexts): assert converter.convert(text) == ans, \ 'Failed to convert {} for {} -> {}'.format(pref, text, ans) + + +if __name__ == "__main__": + sys.exit(pytest.main(sys.argv[1:])) diff --git a/src/BUILD.bazel b/src/BUILD.bazel index 550d3b864..e1e5f24db 100644 --- a/src/BUILD.bazel +++ b/src/BUILD.bazel @@ -1,4 +1,6 @@ +load("@pybind11_bazel//:build_defs.bzl", "pybind_extension") load("@rules_cc//cc:defs.bzl", "cc_library") +load("@rules_python//python:py_library.bzl", "py_library") package(default_visibility = ["//visibility:public"]) @@ -328,6 +330,18 @@ cc_library( ], ) +pybind_extension( + name = "opencc_clib", + srcs = ["py_opencc.cpp"], + deps = [":opencc"], +) + +py_library( + name = "py_opencc", + data = [":opencc_clib"], + imports = ["."], +) + cc_library( name = "segmentation", srcs = ["Segmentation.cpp"],