Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ on:
branches: [master]

env:
# cibuildwheel 3.0 moved PyPy into an opt-in "enable group", which silently
# dropped the PyPy wheels this project had shipped up to 16.0.0 (17.0.0 and
# 17.0.1 published none). Ask for them back explicitly.
CIBW_ENABLE: pypy
CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014
CIBW_MANYLINUX_I686_IMAGE: manylinux2014
CIBW_MANYLINUX_PYPY_X86_64_IMAGE: manylinux2014
Expand Down Expand Up @@ -98,7 +102,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install --upgrade setuptools wheel twine
pip install --upgrade build twine
- name: Download artifacts from build jobs
uses: actions/download-artifact@v4
with:
Expand Down Expand Up @@ -134,7 +138,7 @@ jobs:
draft: false
prerelease: false
- name: Build sdist
run: python setup.py sdist
run: python -m build --sdist
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1

2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Changelog

## 17.0.0
- Upgrade to Unicode 17.0.0

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ Pre-compiled wheel packages are available on [PyPI] and can be installed via pip
Testing
=======

We run the tests using `tox`. This can be installed as usual with `pip install tox`.
We run the tests using `tox`. This can be installed as usual with `pip install tox`,
or with `pip install --group dev` to pick it up from `pyproject.toml`.

Without any options, `tox` will run the tests against all of the library's
target Python versions. Any missing versions will be skipped.
Expand Down
56 changes: 56 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
[build-system]
# setuptools 77.0.1 is the first release that combines declarative ``ext-modules``
# (added in 74.1) with PEP 639 license metadata, which is what lets this
# project drop setup.py entirely.
requires = ["setuptools>=77.0.1"]
build-backend = "setuptools.build_meta"

[project]
name = "unicodedata2"
version = "17.0.1"
description = "Unicodedata backport updated to the latest Unicode version."
authors = [
{ name = "Mike Kaplinskiy", email = "mike.kaplinskiy@gmail.com" },
]
license = "Apache-2.0"
license-files = ["LICENSE"]
# README.md and CHANGELOG.md are concatenated so both are shown on PyPI.
dynamic = ["readme"]

[project.optional-dependencies]
# Installed into every cibuildwheel test environment via CIBW_TEST_EXTRAS, so
# keep it to what "pytest tests/" actually loads. pytest-randomly is a plugin
# and shuffles test order just by being installed; coverage and pytest-xdist
# needed flags nothing passes.
testing = [
"pytest",
"pytest-randomly",
]

# Local development only. PEP 735 dependency groups are never written into
# the published wheel/sdist metadata, so this adds nothing for installers of
# unicodedata2 -- it just gives contributors "pip install --group dev".
[dependency-groups]
dev = ["tox"]

[project.urls]
Homepage = "http://github.com/fonttools/unicodedata2"
Download = "http://github.com/fonttools/unicodedata2"

[tool.setuptools]
platforms = ["any"]
# The distribution is a single top-level extension module: there are no Python
# packages or modules to ship. Saying so explicitly keeps auto-discovery from
# looking at the unicodedata2/ source directory or at tests/.
packages = []

[tool.setuptools.dynamic]
readme = { file = ["README.md", "CHANGELOG.md"], content-type = "text/markdown" }

[[tool.setuptools.ext-modules]]
name = "unicodedata2"
sources = [
"unicodedata2/unicodedata.c",
"unicodedata2/unicodectype.c",
]
include-dirs = ["unicodedata2"]
49 changes: 0 additions & 49 deletions setup.py

This file was deleted.

2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tox]
envlist = py{38,39,310,311,312,313,314}, pypy{37,38,39}
envlist = py{38,39,310,311,312,313,314}, pypy{39,310,311}
skip_missing_interpreters = true

[testenv]
Expand Down
74 changes: 73 additions & 1 deletion unicodedata2/_unicodedata2_compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

/*
* Compatibility shims
*
* This header must be included *after* "Python.h": it detects what the host
* Python's headers already provide and only fills in the gaps.
*/


Expand All @@ -18,8 +21,77 @@ static inline void _Py_SET_TYPE(PyObject *ob, PyTypeObject *type)


#ifdef PYPY_VERSION
#include "pypy_ctype.h"
typedef Py_ssize_t Py_ssize_clean_t;
#endif


/* ----------------------------------------------------------------------- *
* ASCII upper-casing for the character name lookup code.
*
* _gethash() and _cmpname() fold the ASCII case of character names so that
* lookup() is case insensitive. CPython does that with a 256 byte table,
* reachable as Py_TOUPPER() because "Python.h" pulls in <cpython/pyctype.h>
* on every non-limited-API build. PyPy's cpyext headers define neither the
* macro nor the table, so we provide an equivalent one ourselves.
*
* The detection tests Py_TOUPPER, the macro, rather than _Py_ctype_toupper,
* which is a linker symbol the preprocessor cannot see. Because this file
* is included after "Python.h" and nothing else defines Py_TOUPPER first,
* "is the macro defined here?" is exactly "does the host Python provide
* upper-casing?".
*
* Either way the argument is masked with Py_CHARMASK() inside the macro, so
* callers pass a plain char and must not mask it again.
* ----------------------------------------------------------------------- */
#ifdef Py_TOUPPER

#define UNICODEDATA2_TOUPPER(c) Py_TOUPPER(c)

#else

/* Same mapping as CPython's _Py_ctype_toupper: ASCII 'a'-'z' fold to 'A'-'Z',
every other byte value is passed through unchanged. Kept private to
unicodedata2 on purpose -- it deliberately does not define a symbol in
CPython's reserved _Py_ namespace, and nothing outside this extension can
link against it. */
static const unsigned char unicodedata2_toupper_table[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
};

#define UNICODEDATA2_TOUPPER(c) \
(unicodedata2_toupper_table[Py_CHARMASK(c)])

#endif /* Py_TOUPPER */

#endif
35 changes: 0 additions & 35 deletions unicodedata2/pypy_ctype.c

This file was deleted.

11 changes: 0 additions & 11 deletions unicodedata2/pypy_ctype.h

This file was deleted.

4 changes: 2 additions & 2 deletions unicodedata2/unicodedata.c
Original file line number Diff line number Diff line change
Expand Up @@ -890,7 +890,7 @@ _gethash(const char *s, int len, int scale)
unsigned long h = 0;
unsigned long ix;
for (i = 0; i < len; i++) {
h = (h * scale) + (unsigned char) Py_TOUPPER(Py_CHARMASK(s[i]));
h = (h * scale) + (unsigned char) UNICODEDATA2_TOUPPER(s[i]);
ix = h & 0xff000000;
if (ix)
h = (h ^ ((ix>>24) & 0xff)) & 0x00ffffff;
Expand Down Expand Up @@ -1049,7 +1049,7 @@ _cmpname(PyObject *self, int code, const char* name, int namelen)
if (!_getucname(self, code, buffer, NAME_MAXLEN, 1))
return 0;
for (i = 0; i < namelen; i++) {
if (Py_TOUPPER(Py_CHARMASK(name[i])) != buffer[i])
if (UNICODEDATA2_TOUPPER(name[i]) != buffer[i])
return 0;
}
return buffer[namelen] == '\0';
Expand Down