Skip to content

Commit

Permalink
Add type annotations to package
Browse files Browse the repository at this point in the history
Adding type annotations will allow packages consuming certifi to have
access to its typing information. The py.typed data file is include for
PEP 561 compliance.

If the API is ever expanded, the mypy type checker will ensure it is
also typed. For example, right now, typeshed is incomplete after
certifi.contents() was added:

https://github.com/python/typeshed/blob/ac2ef6e8c963a45841ad91e068e06748caa1fce6/stubs/certifi/certifi.pyi
  • Loading branch information
jdufresne committed Oct 27, 2021
1 parent 1c8485c commit 5f09ea8
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 7 deletions.
12 changes: 12 additions & 0 deletions .github/workflows/ci.yml
Expand Up @@ -6,6 +6,18 @@ on:
pull_request: {}

jobs:
mypy:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
- name: Install dependencies
run: pip install mypy
- name: Run mypy
run: mypy --strict certifi

test:
runs-on: ubuntu-latest
strategy:
Expand Down
1 change: 1 addition & 0 deletions certifi/__init__.py
@@ -1,3 +1,4 @@
from .core import contents, where

__all__ = ["contents", "where"]
__version__ = "2021.10.08"
18 changes: 14 additions & 4 deletions certifi/core.py
Expand Up @@ -7,14 +7,16 @@
This module returns the installation location of cacert.pem or its contents.
"""
import os
import types
from typing import Union

try:
from importlib.resources import path as get_path, read_text

_CACERT_CTX = None
_CACERT_PATH = None

def where():
def where() -> str:
# This is slightly terrible, but we want to delay extracting the file
# in cases where we're inside of a zipimport situation until someone
# actually calls where(), but we don't want to re-extract the file
Expand All @@ -40,21 +42,29 @@ def where():


except ImportError:
Package = Union[types.ModuleType, str]
Resource = Union[str, "os.PathLike"]

# This fallback will work for Python versions prior to 3.7 that lack the
# importlib.resources module but relies on the existing `where` function
# so won't address issues with environments like PyOxidizer that don't set
# __file__ on modules.
def read_text(_module, _path, encoding="ascii"):
def read_text(
package: Package,
resource: Resource,
encoding: str = 'utf-8',
errors: str = 'strict'
) -> str:
with open(where(), "r", encoding=encoding) as data:
return data.read()

# If we don't have importlib.resources, then we will just do the old logic
# of assuming we're on the filesystem and munge the path directly.
def where():
def where() -> str:
f = os.path.dirname(__file__)

return os.path.join(f, "cacert.pem")


def contents():
def contents() -> str:
return read_text("certifi", "cacert.pem", encoding="ascii")
Empty file added certifi/py.typed
Empty file.
9 changes: 7 additions & 2 deletions certifi/tests/test_certify.py
Expand Up @@ -7,9 +7,14 @@


class TestCertifi(unittest.TestCase):
def test_cabundle_exists(self):
def test_cabundle_exists(self) -> None:
assert os.path.exists(certifi.where())

def test_read_contents(self):
def test_read_contents(self) -> None:
content = certifi.contents()
assert "-----BEGIN CERTIFICATE-----" in content

def test_py_typed_exists(self) -> None:
assert os.path.exists(
os.path.join(os.path.dirname(certifi.__file__), 'py.typed')
)
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -43,7 +43,7 @@
'certifi',
],
package_dir={'certifi': 'certifi'},
package_data={'certifi': ['*.pem']},
package_data={'certifi': ['*.pem', 'py.typed']},
# data_files=[('certifi', ['certifi/cacert.pem'])],
include_package_data=True,
zip_safe=False,
Expand Down

0 comments on commit 5f09ea8

Please sign in to comment.