├── .coveragerc ├── .editorconfig ├── .github └── workflows │ └── main.yml ├── .pre-commit-config.yaml ├── .readthedocs.yaml ├── .travis └── macOS ├── Dockerfile ├── NEWS.rst ├── README.rst ├── conftest.py ├── docs ├── conf.py ├── history.rst └── index.rst ├── jaraco └── clipboard │ ├── Darwin.py │ ├── Linux.py │ ├── Windows.py │ └── __init__.py ├── mypy.ini ├── pyproject.toml ├── pytest.ini ├── ruff.toml ├── test_all.py ├── towncrier.toml └── tox.ini /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit = 3 | # leading `*/` for pytest-dev/pytest-cov#456 4 | */.tox/* 5 | disable_warnings = 6 | couldnt-parse 7 | 8 | [report] 9 | show_missing = True 10 | exclude_also = 11 | # Exclude common false positives per 12 | # https://coverage.readthedocs.io/en/latest/excluding.html#advanced-exclusion 13 | # Ref jaraco/skeleton#97 and jaraco/skeleton#135 14 | class .*\bProtocol\): 15 | if TYPE_CHECKING: 16 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = tab 6 | indent_size = 4 7 | insert_final_newline = true 8 | end_of_line = lf 9 | 10 | [*.py] 11 | indent_style = space 12 | max_line_length = 88 13 | 14 | [*.{yml,yaml}] 15 | indent_style = space 16 | indent_size = 2 17 | 18 | [*.rst] 19 | indent_style = space 20 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: 4 | merge_group: 5 | push: 6 | branches-ignore: 7 | # temporary GH branches relating to merge queues (jaraco/skeleton#93) 8 | - gh-readonly-queue/** 9 | tags: 10 | # required if branches-ignore is supplied (jaraco/skeleton#103) 11 | - '**' 12 | pull_request: 13 | workflow_dispatch: 14 | 15 | permissions: 16 | contents: read 17 | 18 | env: 19 | # Environment variable to support color support (jaraco/skeleton#66) 20 | FORCE_COLOR: 1 21 | 22 | # Suppress noisy pip warnings 23 | PIP_DISABLE_PIP_VERSION_CHECK: 'true' 24 | PIP_NO_WARN_SCRIPT_LOCATION: 'true' 25 | 26 | # Ensure tests can sense settings about the environment 27 | TOX_OVERRIDE: >- 28 | testenv.pass_env+=GITHUB_*,FORCE_COLOR 29 | 30 | 31 | jobs: 32 | test: 33 | strategy: 34 | # https://blog.jaraco.com/efficient-use-of-ci-resources/ 35 | matrix: 36 | python: 37 | - "3.9" 38 | - "3.13" 39 | platform: 40 | - ubuntu-latest 41 | - macos-latest 42 | - windows-latest 43 | include: 44 | - python: "3.10" 45 | platform: ubuntu-latest 46 | - python: "3.11" 47 | platform: ubuntu-latest 48 | - python: "3.12" 49 | platform: ubuntu-latest 50 | - python: "3.14" 51 | platform: ubuntu-latest 52 | - python: pypy3.10 53 | platform: ubuntu-latest 54 | runs-on: ${{ matrix.platform }} 55 | continue-on-error: ${{ matrix.python == '3.14' }} 56 | steps: 57 | - uses: actions/checkout@v4 58 | - name: Install build dependencies 59 | # Install dependencies for building packages on pre-release Pythons 60 | # jaraco/skeleton#161 61 | if: matrix.python == '3.14' && matrix.platform == 'ubuntu-latest' 62 | run: | 63 | sudo apt update 64 | sudo apt install -y libxml2-dev libxslt-dev 65 | - name: Setup Python 66 | uses: actions/setup-python@v5 67 | with: 68 | python-version: ${{ matrix.python }} 69 | allow-prereleases: true 70 | - name: Install tox 71 | run: python -m pip install tox 72 | - name: Run 73 | run: tox 74 | 75 | collateral: 76 | strategy: 77 | fail-fast: false 78 | matrix: 79 | job: 80 | - diffcov 81 | - docs 82 | runs-on: ubuntu-latest 83 | steps: 84 | - uses: actions/checkout@v4 85 | with: 86 | fetch-depth: 0 87 | - name: Setup Python 88 | uses: actions/setup-python@v5 89 | with: 90 | python-version: 3.x 91 | - name: Install tox 92 | run: python -m pip install tox 93 | - name: Eval ${{ matrix.job }} 94 | run: tox -e ${{ matrix.job }} 95 | 96 | check: # This job does nothing and is only used for the branch protection 97 | if: always() 98 | 99 | needs: 100 | - test 101 | - collateral 102 | 103 | runs-on: ubuntu-latest 104 | 105 | steps: 106 | - name: Decide whether the needed jobs succeeded or failed 107 | uses: re-actors/alls-green@release/v1 108 | with: 109 | jobs: ${{ toJSON(needs) }} 110 | 111 | release: 112 | permissions: 113 | contents: write 114 | needs: 115 | - check 116 | if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') 117 | runs-on: ubuntu-latest 118 | 119 | steps: 120 | - uses: actions/checkout@v4 121 | - name: Setup Python 122 | uses: actions/setup-python@v5 123 | with: 124 | python-version: 3.x 125 | - name: Install tox 126 | run: python -m pip install tox 127 | - name: Run 128 | run: tox -e release 129 | env: 130 | TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} 131 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 132 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/astral-sh/ruff-pre-commit 3 | rev: v0.9.9 4 | hooks: 5 | - id: ruff 6 | args: [--fix, --unsafe-fixes] 7 | - id: ruff-format 8 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | python: 3 | install: 4 | - path: . 5 | extra_requirements: 6 | - doc 7 | 8 | sphinx: 9 | configuration: docs/conf.py 10 | 11 | # required boilerplate readthedocs/readthedocs.org#10401 12 | build: 13 | os: ubuntu-lts-latest 14 | tools: 15 | python: latest 16 | # post-checkout job to ensure the clone isn't shallow jaraco/skeleton#114 17 | jobs: 18 | post_checkout: 19 | - git fetch --unshallow || true 20 | -------------------------------------------------------------------------------- /.travis/macOS: -------------------------------------------------------------------------------- 1 | # Perform the manual steps on macOS to install python3 and activate an environment 2 | brew update 3 | brew upgrade python 4 | rm -f /usr/local/bin/pip 5 | ln -s python3 /usr/local/bin/python 6 | ln -s pip3 /usr/local/bin/pip 7 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/windows/servercore:ltsc2019 2 | RUN powershell -c "Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iwr https://chocolatey.org/install.ps1 -UseBasicParsing | iex" 3 | RUN choco feature enable -n allowGlobalConfirmation 4 | RUN choco install git python 5 | RUN python -m pip install -U pip pipx 6 | RUN setx path "%path%;C:\Users\ContainerAdministrator\.local\bin" 7 | RUN pipx install tox 8 | RUN setx TOX_WORK_DIR C:\tox 9 | CMD powershell 10 | -------------------------------------------------------------------------------- /NEWS.rst: -------------------------------------------------------------------------------- 1 | v3.1.0 2 | ====== 3 | 4 | Switch to native namespace for 'jaraco' package and other refresh. 5 | 6 | v3.0.0 7 | ====== 8 | 9 | #6: Copy/paste HTML now fixed by building on fixes in jaraco.windows. 10 | 11 | Require Python 3.6 or later. 12 | 13 | 2.0.1 14 | ===== 15 | 16 | #4: Fix usage of richxerox for HTML copy operation on macOS. 17 | 18 | 2.0 19 | === 20 | 21 | Switch to `pkgutil namespace technique 22 | `_ 23 | for the ``jaraco`` namespace. 24 | 25 | 1.3.4 26 | ===== 27 | 28 | #3: Fixed paste_html on Windows. 29 | 30 | 1.3.3 31 | ===== 32 | 33 | Bump to richxerox 1.0.1 for declared dependency on pyobjc. 34 | 35 | Run CI tests on macOS and Windows. 36 | 37 | 1.3.2 38 | ===== 39 | 40 | Refresh packaging. 41 | 42 | Update dependency declaration on richxerox. 43 | 44 | Update dependency on pyperclip and fix usage. 45 | 46 | 1.3.1 47 | ===== 48 | 49 | Refresh packaging. 50 | 51 | 1.3 52 | === 53 | 54 | Add support for copy/paste text on Linux via pyperclip. 55 | 56 | 1.2 57 | === 58 | 59 | Added test suite and automated deployments for tagged 60 | commits. 61 | 62 | 1.1 63 | === 64 | 65 | Move hosting to github. 66 | 67 | 1.0 68 | === 69 | 70 | Initial implementation with text and HTML support on Windows 71 | and Mac OS X and image paste support on Windows. 72 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://img.shields.io/pypi/v/jaraco.clipboard.svg 2 | :target: https://pypi.org/project/jaraco.clipboard 3 | 4 | .. image:: https://img.shields.io/pypi/pyversions/jaraco.clipboard.svg 5 | 6 | .. image:: https://github.com/jaraco/jaraco.clipboard/actions/workflows/main.yml/badge.svg 7 | :target: https://github.com/jaraco/jaraco.clipboard/actions?query=workflow%3A%22tests%22 8 | :alt: tests 9 | 10 | .. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json 11 | :target: https://github.com/astral-sh/ruff 12 | :alt: Ruff 13 | 14 | .. image:: https://readthedocs.org/projects/jaracoclipboard/badge/?version=latest 15 | :target: https://jaracoclipboard.readthedocs.io/en/latest/?badge=latest 16 | 17 | .. image:: https://img.shields.io/badge/skeleton-2025-informational 18 | :target: https://blog.jaraco.com/skeleton 19 | 20 | The only clipboard library for Python that supports text on all 21 | three major platforms plus HTML on MacOS and HTML and images 22 | on Windows. 23 | 24 | Similar to `pyperclip `_ 25 | and `clipboard `_ 26 | and `xerox `_ except attempts 27 | to support more formats than just text. 28 | 29 | This library is just a thin wrapper around the best platform implementations: 30 | 31 | - pyperclip for Linux 32 | - richxerox for MacOS 33 | - jaraco.windows for Windows 34 | 35 | Usage 36 | ===== 37 | 38 | ``jaraco.clipboard`` supplies several functions in the clipboard module. 39 | The most common are the copy and paste functions: 40 | 41 | .. code-block:: python 42 | 43 | from jaraco import clipboard 44 | clipboard.copy('some text') 45 | clipboard.paste() == 'some text' 46 | 47 | Other functions include ``copy/paste`` ``html`` and ``image``. 48 | 49 | If not implemented on your platform, the functions will raise 50 | NotImplementedError. 51 | -------------------------------------------------------------------------------- /conftest.py: -------------------------------------------------------------------------------- 1 | import platform 2 | 3 | platform_names = 'Linux', 'Darwin', 'Windows' 4 | 5 | collect_ignore = [ 6 | f'jaraco/clipboard/{name}.py' 7 | for name in platform_names 8 | if platform.system() != name 9 | ] 10 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | extensions = [ 4 | 'sphinx.ext.autodoc', 5 | 'jaraco.packaging.sphinx', 6 | ] 7 | 8 | master_doc = "index" 9 | html_theme = "furo" 10 | 11 | # Link dates and other references in the changelog 12 | extensions += ['rst.linker'] 13 | link_files = { 14 | '../NEWS.rst': dict( 15 | using=dict(GH='https://github.com'), 16 | replace=[ 17 | dict( 18 | pattern=r'(Issue #|\B#)(?P\d+)', 19 | url='{package_url}/issues/{issue}', 20 | ), 21 | dict( 22 | pattern=r'(?m:^((?Pv?\d+(\.\d+){1,2}))\n[-=]+\n)', 23 | with_scm='{text}\n{rev[timestamp]:%d %b %Y}\n', 24 | ), 25 | dict( 26 | pattern=r'PEP[- ](?P\d+)', 27 | url='https://peps.python.org/pep-{pep_number:0>4}/', 28 | ), 29 | ], 30 | ) 31 | } 32 | 33 | # Be strict about any broken references 34 | nitpicky = True 35 | nitpick_ignore: list[tuple[str, str]] = [] 36 | 37 | # Include Python intersphinx mapping to prevent failures 38 | # jaraco/skeleton#51 39 | extensions += ['sphinx.ext.intersphinx'] 40 | intersphinx_mapping = { 41 | 'python': ('https://docs.python.org/3', None), 42 | } 43 | 44 | # Preserve authored syntax for defaults 45 | autodoc_preserve_defaults = True 46 | 47 | # Add support for linking usernames, PyPI projects, Wikipedia pages 48 | github_url = 'https://github.com/' 49 | extlinks = { 50 | 'user': (f'{github_url}%s', '@%s'), 51 | 'pypi': ('https://pypi.org/project/%s', '%s'), 52 | 'wiki': ('https://wikipedia.org/wiki/%s', '%s'), 53 | } 54 | extensions += ['sphinx.ext.extlinks'] 55 | 56 | # local 57 | -------------------------------------------------------------------------------- /docs/history.rst: -------------------------------------------------------------------------------- 1 | :tocdepth: 2 2 | 3 | .. _changes: 4 | 5 | History 6 | ******* 7 | 8 | .. include:: ../NEWS (links).rst 9 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to |project| documentation! 2 | =================================== 3 | 4 | .. sidebar-links:: 5 | :home: 6 | :pypi: 7 | 8 | .. toctree:: 9 | :maxdepth: 1 10 | 11 | history 12 | 13 | 14 | .. automodule:: jaraco.clipboard 15 | :members: 16 | :undoc-members: 17 | :show-inheritance: 18 | 19 | 20 | Indices and tables 21 | ================== 22 | 23 | * :ref:`genindex` 24 | * :ref:`modindex` 25 | * :ref:`search` 26 | 27 | -------------------------------------------------------------------------------- /jaraco/clipboard/Darwin.py: -------------------------------------------------------------------------------- 1 | import functools 2 | 3 | import richxerox 4 | 5 | paste_text = richxerox.paste 6 | copy_text = richxerox.copy 7 | 8 | paste_html = functools.partial(richxerox.paste, format='html') 9 | 10 | 11 | def copy_html(content): 12 | return richxerox.copy(html=content) 13 | -------------------------------------------------------------------------------- /jaraco/clipboard/Linux.py: -------------------------------------------------------------------------------- 1 | import pyperclip 2 | 3 | copy_text, paste_text = pyperclip.determine_clipboard() 4 | 5 | unavailable = any( 6 | isinstance(func, object) and type(func).__name__ == 'ClipboardUnavailable' 7 | for func in locals().values() 8 | ) 9 | 10 | if unavailable: 11 | del copy_text 12 | del paste_text 13 | -------------------------------------------------------------------------------- /jaraco/clipboard/Windows.py: -------------------------------------------------------------------------------- 1 | import jaraco.windows.clipboard as wclip 2 | 3 | copy_text = wclip.set_unicode_text 4 | paste_text = wclip.get_unicode_text 5 | 6 | copy_html = wclip.set_html 7 | 8 | 9 | def paste_html(): 10 | return wclip.get_html().fragment 11 | 12 | 13 | paste_image = wclip.get_image 14 | -------------------------------------------------------------------------------- /jaraco/clipboard/__init__.py: -------------------------------------------------------------------------------- 1 | import platform 2 | import importlib 3 | import itertools 4 | 5 | 6 | def _not_implemented(*args, **kwargs): 7 | raise NotImplementedError("format not supported") 8 | 9 | 10 | def _init(): 11 | _platform_mod_name = '.' + platform.system() 12 | _platform_mod = importlib.import_module(_platform_mod_name, 'jaraco.clipboard') 13 | 14 | # support copy and paste of text, html, and image. 15 | _modes = 'copy', 'paste' 16 | _formats = 'text', 'html', 'image' 17 | _methods = map('_'.join, itertools.product(_modes, _formats)) 18 | 19 | for name in _methods: 20 | func = getattr(_platform_mod, name, _not_implemented) 21 | globals().update({name: func}) 22 | 23 | globals().update(copy=globals()['copy_text']) 24 | globals().update(paste=globals()['paste_text']) 25 | 26 | 27 | _init() 28 | 29 | 30 | __all__ = [ 31 | name 32 | for name, func in globals().items() 33 | if callable(func) and not name.startswith('_') 34 | ] 35 | -------------------------------------------------------------------------------- /mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | # Is the project well-typed? 3 | strict = False 4 | 5 | # Early opt-in even when strict = False 6 | warn_unused_ignores = True 7 | warn_redundant_casts = True 8 | enable_error_code = ignore-without-code 9 | 10 | # Support namespace packages per https://github.com/python/mypy/issues/14057 11 | explicit_package_bases = True 12 | 13 | disable_error_code = 14 | # Disable due to many false positives 15 | overload-overlap, 16 | 17 | # asweigart/pyperclip#210 18 | [mypy-pyperclip] 19 | ignore_missing_imports = True 20 | 21 | # jonathaneunice/richxerox#3 22 | [mypy-richxerox] 23 | ignore_missing_imports = True 24 | 25 | # jaraco/jaraco.windows#26 26 | [mypy-jaraco.windows.*] 27 | ignore_missing_imports = True 28 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "setuptools>=77", 4 | "setuptools_scm[toml]>=3.4.1", 5 | # jaraco/skeleton#174 6 | "coherent.licensed", 7 | ] 8 | build-backend = "setuptools.build_meta" 9 | 10 | [project] 11 | name = "jaraco.clipboard" 12 | authors = [ 13 | { name = "Jason R. Coombs", email = "jaraco@jaraco.com" }, 14 | ] 15 | description = "Multi-format, cross-platform clipboard library" 16 | readme = "README.rst" 17 | classifiers = [ 18 | "Development Status :: 5 - Production/Stable", 19 | "Intended Audience :: Developers", 20 | "Programming Language :: Python :: 3", 21 | "Programming Language :: Python :: 3 :: Only", 22 | ] 23 | requires-python = ">=3.9" 24 | license = "MIT" 25 | dependencies = [ 26 | 'jaraco.windows >= 5.1; sys_platform=="win32"', 27 | 'richxerox >= 1.0.1; sys_platform=="darwin"', 28 | 'pyperclip >= 1.6; sys_platform=="linux2" or sys_platform=="linux"', 29 | ] 30 | dynamic = ["version"] 31 | 32 | [project.urls] 33 | Source = "https://github.com/jaraco/jaraco.clipboard" 34 | 35 | [project.optional-dependencies] 36 | test = [ 37 | # upstream 38 | "pytest >= 6, != 8.1.*", 39 | 40 | # local 41 | "pygments", 42 | ] 43 | 44 | doc = [ 45 | # upstream 46 | "sphinx >= 3.5", 47 | "jaraco.packaging >= 9.3", 48 | "rst.linker >= 1.9", 49 | "furo", 50 | "sphinx-lint", 51 | 52 | # local 53 | ] 54 | 55 | check = [ 56 | "pytest-checkdocs >= 2.4", 57 | "pytest-ruff >= 0.2.1; sys_platform != 'cygwin'", 58 | ] 59 | 60 | cover = [ 61 | "pytest-cov", 62 | ] 63 | 64 | enabler = [ 65 | "pytest-enabler >= 2.2", 66 | ] 67 | 68 | type = [ 69 | # upstream 70 | "pytest-mypy", 71 | 72 | # local 73 | ] 74 | 75 | 76 | [tool.setuptools_scm] 77 | 78 | 79 | [tool.pytest-enabler.mypy] 80 | # Disabled due to jaraco/skeleton#143 81 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | norecursedirs=dist build .tox .eggs 3 | addopts= 4 | --doctest-modules 5 | --import-mode importlib 6 | consider_namespace_packages=true 7 | filterwarnings= 8 | ## upstream 9 | 10 | # Ensure ResourceWarnings are emitted 11 | default::ResourceWarning 12 | 13 | # realpython/pytest-mypy#152 14 | ignore:'encoding' argument not specified::pytest_mypy 15 | 16 | # python/cpython#100750 17 | ignore:'encoding' argument not specified::platform 18 | 19 | # pypa/build#615 20 | ignore:'encoding' argument not specified::build.env 21 | 22 | # dateutil/dateutil#1284 23 | ignore:datetime.datetime.utcfromtimestamp:DeprecationWarning:dateutil.tz.tz 24 | 25 | ## end upstream 26 | -------------------------------------------------------------------------------- /ruff.toml: -------------------------------------------------------------------------------- 1 | [lint] 2 | extend-select = [ 3 | # upstream 4 | 5 | "C901", # complex-structure 6 | "I", # isort 7 | "PERF401", # manual-list-comprehension 8 | 9 | # Ensure modern type annotation syntax and best practices 10 | # Not including those covered by type-checkers or exclusive to Python 3.11+ 11 | "FA", # flake8-future-annotations 12 | "F404", # late-future-import 13 | "PYI", # flake8-pyi 14 | "UP006", # non-pep585-annotation 15 | "UP007", # non-pep604-annotation 16 | "UP010", # unnecessary-future-import 17 | "UP035", # deprecated-import 18 | "UP037", # quoted-annotation 19 | "UP043", # unnecessary-default-type-args 20 | 21 | # local 22 | ] 23 | ignore = [ 24 | # upstream 25 | 26 | # Typeshed rejects complex or non-literal defaults for maintenance and testing reasons, 27 | # irrelevant to this project. 28 | "PYI011", # typed-argument-default-in-stub 29 | # https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules 30 | "W191", 31 | "E111", 32 | "E114", 33 | "E117", 34 | "D206", 35 | "D300", 36 | "Q000", 37 | "Q001", 38 | "Q002", 39 | "Q003", 40 | "COM812", 41 | "COM819", 42 | 43 | # local 44 | ] 45 | 46 | [format] 47 | # Enable preview to get hugged parenthesis unwrapping and other nice surprises 48 | # See https://github.com/jaraco/skeleton/pull/133#issuecomment-2239538373 49 | preview = true 50 | # https://docs.astral.sh/ruff/settings/#format_quote-style 51 | quote-style = "preserve" 52 | -------------------------------------------------------------------------------- /test_all.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from jaraco import clipboard 4 | 5 | 6 | def needs_command(name): 7 | cmd = vars(clipboard)[name] 8 | 9 | return pytest.mark.xfail( 10 | cmd is clipboard._not_implemented, 11 | reason="{name} not implemented on this platform", 12 | ) 13 | 14 | 15 | @needs_command("copy_text") 16 | def test_copy_text(): 17 | clipboard.copy_text("foo") 18 | 19 | 20 | @needs_command("paste_text") 21 | def test_paste_text(): 22 | # first ensure there's something on the clipboard: 23 | clipboard.copy_text('test for paste') 24 | assert clipboard.paste_text() == 'test for paste' 25 | 26 | 27 | html_sample = '

Your paragraph here

' 28 | 29 | 30 | @needs_command("copy_html") 31 | def test_copy_html(): 32 | clipboard.copy_html(html_sample) 33 | 34 | 35 | @pytest.mark.xfail('platform.system() == "Windows"', reason="#6") 36 | @needs_command("paste_html") 37 | def test_paste_html(): 38 | # first ensure there's something on the clipboard 39 | clipboard.copy_html(html_sample) 40 | assert clipboard.paste_html() == html_sample 41 | 42 | 43 | def test_not_implemented(): 44 | """ 45 | Execute _not_implemented for coverage. 46 | """ 47 | with pytest.raises(NotImplementedError): 48 | clipboard._not_implemented() 49 | -------------------------------------------------------------------------------- /towncrier.toml: -------------------------------------------------------------------------------- 1 | [tool.towncrier] 2 | title_format = "{version}" 3 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [testenv] 2 | description = perform primary checks (tests, style, types, coverage) 3 | deps = 4 | setenv = 5 | PYTHONWARNDEFAULTENCODING = 1 6 | commands = 7 | pytest {posargs} 8 | usedevelop = True 9 | extras = 10 | test 11 | check 12 | cover 13 | enabler 14 | type 15 | 16 | [testenv:diffcov] 17 | description = run tests and check that diff from main is covered 18 | deps = 19 | {[testenv]deps} 20 | diff-cover 21 | commands = 22 | pytest {posargs} --cov-report xml 23 | diff-cover coverage.xml --compare-branch=origin/main --html-report diffcov.html 24 | diff-cover coverage.xml --compare-branch=origin/main --fail-under=100 25 | 26 | [testenv:docs] 27 | description = build the documentation 28 | extras = 29 | doc 30 | test 31 | changedir = docs 32 | commands = 33 | python -m sphinx -W --keep-going . {toxinidir}/build/html 34 | python -m sphinxlint 35 | 36 | [testenv:finalize] 37 | description = assemble changelog and tag a release 38 | skip_install = True 39 | deps = 40 | towncrier 41 | jaraco.develop >= 7.23 42 | pass_env = * 43 | commands = 44 | python -m jaraco.develop.finalize 45 | 46 | 47 | [testenv:release] 48 | description = publish the package to PyPI and GitHub 49 | skip_install = True 50 | deps = 51 | build 52 | twine>=3 53 | jaraco.develop>=7.1 54 | pass_env = 55 | TWINE_PASSWORD 56 | GITHUB_TOKEN 57 | setenv = 58 | TWINE_USERNAME = {env:TWINE_USERNAME:__token__} 59 | commands = 60 | python -c "import shutil; shutil.rmtree('dist', ignore_errors=True)" 61 | python -m build 62 | python -m twine upload dist/* 63 | python -m jaraco.develop.create-github-release 64 | --------------------------------------------------------------------------------