Repository: jdkandersson/OpenAlchemy

Issue:
Add support for Python 3.9
As a user I want to use Python 3.9 when it is released so that I can use all the new features.

Add testing for python 3.9.

Discussion:

Patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index a9de7825..b8c31e59 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -4,7 +4,6 @@ repos:
     hooks:
       - id: black
         language_version: python3.7
-        exclude: "models_auto.py$|models_autogenerated.py$"
   - repo: https://github.com/pre-commit/mirrors-isort
     rev: v5.4.2
     hooks:
@@ -22,14 +21,10 @@ repos:
         exclude: "^example|conf.py$"
         additional_dependencies:
           - "sqlalchemy-stubs"
-  - repo: local
+  - repo: https://github.com/pre-commit/mirrors-pylint
+    rev: "v2.6.0"
     hooks:
       - id: pylint
-        name: pylint
-        entry: python3 -m pylint.__main__
-        args: [-rn]
-        language: system
-        types: [python]
         exclude: "^example|conf.py$"
   - repo: https://github.com/prettier/prettier
     rev: "2.1.1"
diff --git a/.pylintrc b/.pylintrc
index 903f8c42..c05a0967 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -67,7 +67,7 @@ confidence=
 # no Warning level messages displayed, use"--disable=all --enable=classes
 # --disable=W"
 # disable=long-suffix,standarderror-builtin,indexing-exception,delslice-method,unichr-builtin,dict-view-method,parameter-unpacking,unicode-builtin,cmp-builtin,intern-builtin,round-builtin,backtick,nonzero-method,xrange-builtin,coerce-method,raw_input-builtin,old-division,filter-builtin-not-iterating,old-octal-literal,input-builtin,map-builtin-not-iterating,buffer-builtin,basestring-builtin,zip-builtin-not-iterating,using-cmp-argument,unpacking-in-except,old-raise-syntax,coerce-builtin,dict-iter-method,hex-method,range-builtin-not-iterating,useless-suppression,cmp-method,print-statement,reduce-builtin,file-builtin,long-builtin,getslice-method,execfile-builtin,no-absolute-import,metaclass-assignment,oct-method,reload-builtin,import-star-module-level,suppressed-message,apply-builtin,raising-string,next-method-called,setslice-method,old-ne-operator,arguments-differ,wildcard-import,locally-disabled
-disable=duplicate-code,bad-continuation
+disable=duplicate-code,bad-continuation,import-error,unsubscriptable-object
 
 
 [REPORTS]
diff --git a/.python-version b/.python-version
index 1a93927c..680a41c6 100644
--- a/.python-version
+++ b/.python-version
@@ -1,3 +1,3 @@
-3.8.5
-3.7.4
-3.6.9
+3.9.0
+3.8.6
+3.7.9
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 21eb76cd..8e44ad52 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@
 - Add `build_json` and `build_yaml` interfaces which can be used to produce a package with the models.
 - Changed output of build_json and build_yaml to be contained within a project directory.
 - Add support for building sdist or wheel distributable archive.
+- Drop support for Python 3.6 and add support for Python 3.9.
 
 ## Version 1.5.4 - 2020-08-30
 
diff --git a/README.md b/README.md
index dfbed863..2d12de6d 100644
--- a/README.md
+++ b/README.md
@@ -128,7 +128,7 @@ An example API has been defined using connexion and Flask here:
 - `date-time`,
 - generic JSON data,
 - `$ref` references for columns and models,
-- remote `$ref` to other files on the same file system,
+- remote `$ref` to other files on the same file system (_not supported on Windows_),
 - remote `$ref` to other files at a URL,
 - primary keys,
 - auto incrementing,
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index f11a0dbb..3676e914 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -17,15 +17,25 @@ schedules:
 jobs:
   - job: "pytest"
     pool:
-      vmImage: "ubuntu-latest"
+      vmImage: "$(imageName)"
     strategy:
       matrix:
-        Python36:
-          python.version: "3.6"
         Python37:
           python.version: "3.7"
-        Python38:
+          imageName: "ubuntu-latest"
+          PIP_CACHE_DIR: $(Pipeline.Workspace)/.pip
+        Python38Linux:
+          python.version: "3.8"
+          imageName: "ubuntu-latest"
+          PIP_CACHE_DIR: $(Pipeline.Workspace)/.pip
+        Python38Mac:
           python.version: "3.8"
+          imageName: "macOS-latest"
+          PIP_CACHE_DIR: $(Pipeline.Workspace)/.pip
+        Python38Windows:
+          python.version: "3.8"
+          imageName: "windows-latest"
+          PIP_CACHE_DIR: $(Pipeline.Workspace)\.pip
 
     steps:
       - task: UsePythonVersion@0
@@ -33,6 +43,12 @@ jobs:
           versionSpec: "$(python.version)"
         displayName: "Use Python $(python.version)"
 
+      - task: Cache@2
+        inputs:
+          key: '"$(python.version)" | "$(Agent.OS)" | setup.py'
+          path: $(PIP_CACHE_DIR)
+        displayName: Cache pip packages
+
       - script: |
           python -m pip install --upgrade pip
           python -m pip install -e .[dev,test]
diff --git a/docs/source/technical_details/references.rst b/docs/source/technical_details/references.rst
index 8133f0d4..a43509d4 100644
--- a/docs/source/technical_details/references.rst
+++ b/docs/source/technical_details/references.rst
@@ -45,6 +45,10 @@ The OpenAPI specification supports remote references in
 1. another file within the file system and
 2. a file available at a URL.
 
+  .. warning::
+
+    (1) is not supported on Windows.
+
 For (1), the following example shows how to reference a schema in another file:
 
 .. literalinclude:: ../../../examples/remote/example-spec.yml
diff --git a/open_alchemy/__init__.py b/open_alchemy/__init__.py
index f4bfa507..16299b7e 100644
--- a/open_alchemy/__init__.py
+++ b/open_alchemy/__init__.py
@@ -7,7 +7,6 @@ import typing
 
 from sqlalchemy.ext import declarative
 
-from open_alchemy import build
 from open_alchemy import types as oa_types
 
 from . import build as _build_module
@@ -16,6 +15,7 @@ from . import helpers as _helpers
 from . import model_factory as _model_factory
 from . import models_file as _models_file
 from . import schemas as _schemas_module
+from .build import PackageFormat
 
 models = py_types.ModuleType("models")  # pylint: disable=invalid-name
 sys.modules["open_alchemy.models"] = models
@@ -251,7 +251,7 @@ def build_json(
     spec_filename: str,
     package_name: str,
     dist_path: str,
-    format_: build.PackageFormat = build.PackageFormat.NONE,
+    format_: PackageFormat = PackageFormat.NONE,
 ) -> None:
     """
     Create an OpenAlchemy distribution package with the SQLAlchemy models.
@@ -289,7 +289,7 @@ def build_yaml(
     spec_filename: str,
     package_name: str,
     dist_path: str,
-    format_: build.PackageFormat = build.PackageFormat.NONE,
+    format_: PackageFormat = PackageFormat.NONE,
 ) -> None:
     """
     Create an OpenAlchemy distribution package with the SQLAlchemy models.
@@ -326,4 +326,11 @@ def build_yaml(
     )
 
 
-__all__ = ["init_model_factory", "init_json", "init_yaml", "build_json", "build_yaml"]
+__all__ = [
+    "init_model_factory",
+    "init_json",
+    "init_yaml",
+    "build_json",
+    "build_yaml",
+    "PackageFormat",
+]
diff --git a/open_alchemy/build/__init__.py b/open_alchemy/build/__init__.py
index 8374d001..6b72bb91 100644
--- a/open_alchemy/build/__init__.py
+++ b/open_alchemy/build/__init__.py
@@ -1,14 +1,9 @@
 """Build a package with the OpenAlchemy models."""
+
 import enum
 import hashlib
 import json
 import pathlib
-import shlex
-
-
-# TODO(rgreinho)): Fix it in the future when following  # pylint: disable=W0511
-# issue is resolved:
-# https://github.com/PyCQA/bandit/issues/211
 import subprocess  # nosec: we are aware of the implications.
 import sys
 import typing
@@ -20,6 +15,11 @@ from .. import models_file as models_file_module
 from .. import schemas as schemas_module
 from .. import types
 
+# TODO(rgreinho)): Fix above nosec it in the future when  # pylint: disable=W0511
+# following issue is resolved:
+# https://github.com/PyCQA/bandit/issues/211
+
+
 _DIRECTORY = pathlib.Path(__file__).parent.absolute()
 with open(_DIRECTORY / "setup.j2") as in_file:
     _SETUP_TEMPLATE = in_file.read()
@@ -254,7 +254,7 @@ def dump(
 
 def build_dist(name: str, path: str, format_: PackageFormat) -> None:
     """
-    Build a distibution package.
+    Build a distribution package.
 
     The formats can be combined with the bitwise operator "or" (``|``), for
     instance, building both sdist and wheel packages can be specified like this:
@@ -283,7 +283,7 @@ def build_sdist(name: str, path: str) -> None:
         path: The package directory.
     """
     pkg_dir = pathlib.Path(path) / name
-    run(f"{sys.executable} setup.py sdist", str(pkg_dir))
+    run([sys.executable, "setup.py", "sdist"], str(pkg_dir))
 
 
 def build_wheel(name: str, path: str) -> None:
@@ -297,7 +297,7 @@ def build_wheel(name: str, path: str) -> None:
     """
     pkg_dir = pathlib.Path(path) / name
     try:
-        run(f"{sys.executable} setup.py bdist_wheel", str(pkg_dir))
+        run([sys.executable, "setup.py", "bdist_wheel"], str(pkg_dir))
     except exceptions.BuildError as exc:
         raise RuntimeError(
             "Building a wheel package requires the wheel package. "
@@ -305,7 +305,7 @@ def build_wheel(name: str, path: str) -> None:
         ) from exc
 
 
-def run(cmd: str, cwd: str) -> typing.Tuple[str, str]:
+def run(cmd: typing.List[str], cwd: str) -> typing.Tuple[str, str]:
     """
     Run a shell command.
 
@@ -315,6 +315,7 @@ def run(cmd: str, cwd: str) -> typing.Tuple[str, str]:
 
     Returns:
         A tuple containing (stdout, stderr).
+
     """
     output = None
     try:
@@ -322,7 +323,7 @@ def run(cmd: str, cwd: str) -> typing.Tuple[str, str]:
         # subprocess securely:
         # https://security.openstack.org/guidelines/dg_use-subprocess-securely.html
         output = subprocess.run(  # nosec
-            shlex.split(cmd),
+            cmd,
             cwd=cwd,
             check=True,
             shell=False,
diff --git a/open_alchemy/helpers/oa_to_py_type.py b/open_alchemy/helpers/oa_to_py_type.py
index 46997050..cb25d3dc 100644
--- a/open_alchemy/helpers/oa_to_py_type.py
+++ b/open_alchemy/helpers/oa_to_py_type.py
@@ -1,18 +1,11 @@
 """Convert an OpenAPI value based on the format to the equivalent Python value."""
 
 import datetime
-import sys
 import typing
 
 from open_alchemy import exceptions
 from open_alchemy import types
 
-if sys.version_info[1] < 7:
-    # pylint: disable=import-error
-    from backports.datetime_fromisoformat import MonkeyPatch
-
-    MonkeyPatch.patch_fromisoformat()
-
 
 def convert(
     *, value: types.TColumnDefault, type_: str, format_: typing.Optional[str]
diff --git a/open_alchemy/models_file/models/__init__.py b/open_alchemy/models_file/models/__init__.py
index d3f34535..65cfe974 100644
--- a/open_alchemy/models_file/models/__init__.py
+++ b/open_alchemy/models_file/models/__init__.py
@@ -6,11 +6,6 @@ import typing
 
 import jinja2
 
-from open_alchemy import helpers
-from open_alchemy import types as oa_types
-
-from .. import types
-
 _DIRECTORY = os.path.dirname(__file__)
 _TEMPLATE_FILE = os.path.join(_DIRECTORY, "template.j2")
 with open(_TEMPLATE_FILE) as in_file:
diff --git a/open_alchemy/utility_base/from_dict/simple.py b/open_alchemy/utility_base/from_dict/simple.py
index 24af1a72..e37ee172 100644
--- a/open_alchemy/utility_base/from_dict/simple.py
+++ b/open_alchemy/utility_base/from_dict/simple.py
@@ -1,19 +1,12 @@
 """Convert simple type from dictionary to the column equivalent."""
 
 import datetime
-import sys
 
 from ... import exceptions
 from ... import helpers
 from ... import types as oa_types
 from .. import types
 
-if sys.version_info[1] < 7:
-    # pylint: disable=import-error
-    from backports.datetime_fromisoformat import MonkeyPatch
-
-    MonkeyPatch.patch_fromisoformat()
-
 
 def convert(
     value: types.TOptSimpleDict, *, schema: oa_types.Schema
diff --git a/setup.py b/setup.py
index b9835796..d7c57f86 100644
--- a/setup.py
+++ b/setup.py
@@ -17,9 +17,9 @@ setuptools.setup(
     packages=setuptools.find_packages(),
     classifiers=[
         "Programming Language :: Python :: 3",
-        "Programming Language :: Python :: 3.6",
         "Programming Language :: Python :: 3.7",
         "Programming Language :: Python :: 3.8",
+        "Programming Language :: Python :: 3.9",
         "License :: OSI Approved :: Apache Software License",
         "Operating System :: OS Independent",
         "Development Status :: 5 - Production/Stable",
@@ -29,7 +29,7 @@ setuptools.setup(
         "Topic :: Internet",
         "Typing :: Typed",
     ],
-    python_requires=">=3.6",
+    python_requires=">=3.7",
     install_requires=[
         "SQLAlchemy>=1.0",
         "jsonschema>=3",
@@ -69,10 +69,6 @@ setuptools.setup(
             "sqlalchemy_mixins",
             "wheel",
         ],
-        ":python_version<'3.7'": [
-            "dataclasses>=0.7",
-            "backports-datetime-fromisoformat>=1.0.0",
-        ],
         ":python_version<'3.8'": ["typing_extensions>=3.7.4"],
     },
 )


Test patch:
diff --git a/tests/open_alchemy/helpers/test_ref.py b/tests/open_alchemy/helpers/test_ref.py
index db6cdd2b..259060d9 100644
--- a/tests/open_alchemy/helpers/test_ref.py
+++ b/tests/open_alchemy/helpers/test_ref.py
@@ -1,6 +1,7 @@
 """Tests for ref."""
 
 import os
+import sys
 from unittest import mock
 from urllib import error
 
@@ -157,6 +158,9 @@ def test_resolve_error(schema, schemas, exception):
     ],
     ids=["no norm", "norm", "URL"],
 )
+@pytest.mark.xfail(
+    condition=sys.platform == "win32", reason="feature not supported on Windows"
+)
 @pytest.mark.helper
 def test_norm_context(context, expected_context):
     """
@@ -234,127 +238,160 @@ class TestAddRemoteContext:
     @pytest.mark.parametrize(
         "context, ref, expected_ref",
         [
-            ("doc1.ext", "#/Schema", "doc1.ext#/Schema"),
-            ("dir1/doc1.ext", "#/Schema", "dir1/doc1.ext#/Schema"),
-            ("doc1.ext", "doc2.ext#/Schema", "doc2.ext#/Schema"),
-            ("dir1/doc1.ext", "doc2.ext#/Schema", "dir1/doc2.ext#/Schema"),
-            ("doc1.ext", "dir2/doc2.ext#/Schema", "dir2/doc2.ext#/Schema"),
-            ("dir1/doc1.ext", "dir2/doc2.ext#/Schema", "dir1/dir2/doc2.ext#/Schema"),
-            ("doc1.ext", "dir2/../doc2.ext#/Schema", "doc2.ext#/Schema"),
-            ("dir1/doc1.ext", "../doc2.ext#/Schema", "doc2.ext#/Schema"),
-            (
+            pytest.param(
+                "doc1.ext",
+                "#/Schema",
+                "doc1.ext#/Schema",
+                id="within document context document",
+            ),
+            pytest.param(
+                "dir1/doc1.ext",
+                "#/Schema",
+                "dir1/doc1.ext#/Schema",
+                id="within document context folder",
+            ),
+            pytest.param(
+                "doc1.ext",
+                "doc2.ext#/Schema",
+                "doc2.ext#/Schema",
+                id="external same folder context document",
+            ),
+            pytest.param(
+                "dir1/doc1.ext",
+                "doc2.ext#/Schema",
+                "dir1/doc2.ext#/Schema",
+                id="external same folder context folder",
+            ),
+            pytest.param(
+                "doc1.ext",
+                "dir2/doc2.ext#/Schema",
+                "dir2/doc2.ext#/Schema",
+                id="external different folder context document",
+            ),
+            pytest.param(
+                "dir1/doc1.ext",
+                "dir2/doc2.ext#/Schema",
+                "dir1/dir2/doc2.ext#/Schema",
+                id="external different folder context folder",
+            ),
+            pytest.param(
+                "doc1.ext",
+                "dir2/../doc2.ext#/Schema",
+                "doc2.ext#/Schema",
+                id="external different folder require normalization context document",
+            ),
+            pytest.param(
+                "dir1/doc1.ext",
+                "../doc2.ext#/Schema",
+                "doc2.ext#/Schema",
+                id="external different folder require normalization context folder",
+            ),
+            pytest.param(
                 "http://host.com/doc1.ext",
                 "#/Schema",
                 "http://host.com/doc1.ext#/Schema",
+                id="http within document context document",
             ),
-            (
+            pytest.param(
                 "http://host.com/dir1/doc1.ext",
                 "#/Schema",
                 "http://host.com/dir1/doc1.ext#/Schema",
+                id="http within document context folder",
             ),
-            (
+            pytest.param(
                 "HTTP://host.com/doc1.ext",
                 "#/Schema",
                 "HTTP://host.com/doc1.ext#/Schema",
+                id="http capitalized context document",
             ),
-            (
+            pytest.param(
                 "https://host.com/doc1.ext",
                 "#/Schema",
                 "https://host.com/doc1.ext#/Schema",
+                id="https context document",
             ),
-            (
+            pytest.param(
                 "HTTPS://host.com/doc1.ext",
                 "#/Schema",
                 "HTTPS://host.com/doc1.ext#/Schema",
+                id="https capitalized context document",
             ),
-            (
+            pytest.param(
                 "http://host.com/doc1.ext",
                 "doc2.ext#/Schema",
                 "http://host.com/doc2.ext#/Schema",
+                id="http same folder context document",
             ),
-            (
+            pytest.param(
                 "http://host.com/dir1/doc1.ext",
                 "doc2.ext#/Schema",
                 "http://host.com/dir1/doc2.ext#/Schema",
+                id="http same folder context folder",
             ),
-            (
+            pytest.param(
                 "http://host.com/doc1.ext",
                 "dir2/doc2.ext#/Schema",
                 "http://host.com/dir2/doc2.ext#/Schema",
+                id="http different folder context document",
             ),
-            (
+            pytest.param(
                 "http://host.com/dir1/doc1.ext",
                 "dir2/doc2.ext#/Schema",
                 "http://host.com/dir1/dir2/doc2.ext#/Schema",
+                id="http different folder context folder",
             ),
-            (
+            pytest.param(
                 "http://host.com/doc1.ext",
                 "dir2/../doc2.ext#/Schema",
                 "http://host.com/doc2.ext#/Schema",
+                id="http different folder require normalization context document",
             ),
-            (
+            pytest.param(
                 "http://host.com/dir1/doc1.ext",
                 "../doc2.ext#/Schema",
                 "http://host.com/doc2.ext#/Schema",
+                id="http different folder require normalization context folder",
             ),
-            (
+            pytest.param(
                 "http://host1.com/doc1.ext",
                 "http://host2.com/doc1.ext#/Schema",
                 "http://host2.com/doc1.ext#/Schema",
+                id="http other http context document",
             ),
-            (
+            pytest.param(
                 "http://host1.com/doc1.ext",
                 "HTTP://host2.com/doc1.ext#/Schema",
                 "HTTP://host2.com/doc1.ext#/Schema",
+                id="http other http capitalized context document",
             ),
-            (
+            pytest.param(
                 "http://host1.com/doc1.ext",
                 "https://host2.com/doc1.ext#/Schema",
                 "https://host2.com/doc1.ext#/Schema",
+                id="http other https context document",
             ),
-            (
+            pytest.param(
                 "http://host1.com/doc1.ext",
                 "HTTPS://host2.com/doc1.ext#/Schema",
                 "HTTPS://host2.com/doc1.ext#/Schema",
+                id="http other https capitalized context document",
             ),
-            (
+            pytest.param(
                 "http://host1.com/doc1.ext",
                 "//host2.com/doc1.ext#/Schema",
                 "http://host2.com/doc1.ext#/Schema",
+                id="http other network no protocol context document",
             ),
-            (
+            pytest.param(
                 "doc1.ext",
                 "http://host.com/doc1.ext#/Schema",
                 "http://host.com/doc1.ext#/Schema",
+                id="within document to http context document",
             ),
         ],
-        ids=[
-            "within document                                 context document",
-            "                                                context folder",
-            "external same folder                            context document",
-            "                                                context folder",
-            "external different folder                       context document",
-            "                                                context folder",
-            "external different folder require normalization context document",
-            "                                                context folder",
-            "http within document                            context document",
-            "                                                context folder",
-            "http capitalized                                context document",
-            "https                                           context document",
-            "https capitalized                               context document",
-            "http same folder                                context document",
-            "                                                context folder",
-            "http different folder                           context document",
-            "                                                context folder",
-            "http different folder require normalization     context document",
-            "                                                context folder",
-            "http other http                                 context document",
-            "http other http capitalized                     context document",
-            "http other https                                context document",
-            "http other https capitalized                    context document",
-            "http other network no protocol                  context document",
-            "within document to http                         context document",
-        ],
+    )
+    @pytest.mark.xfail(
+        condition=sys.platform == "win32", reason="feature not supported on Windows"
     )
     @pytest.mark.helper
     def test_add_remote_context(context, ref, expected_ref):
@@ -762,6 +799,9 @@ def test_get_remote_ref_ref(tmp_path, _clean_remote_schemas_store):
     assert name == "Schema1"
 
 
+@pytest.mark.xfail(
+    condition=sys.platform == "win32", reason="feature not supported on Windows"
+)
 @pytest.mark.helper
 def test_get_remote_ref_remote_ref(tmp_path, _clean_remote_schemas_store):
     """
diff --git a/tests/open_alchemy/helpers/test_schema.py b/tests/open_alchemy/helpers/test_schema.py
index 8183c265..a3051f0d 100644
--- a/tests/open_alchemy/helpers/test_schema.py
+++ b/tests/open_alchemy/helpers/test_schema.py
@@ -63,10 +63,10 @@ from open_alchemy import helpers
     ],
 )
 @pytest.mark.helper
-def test_constractable(schema, schemas, expected_result):
+def test_constructable(schema, schemas, expected_result):
     """
     GIVEN schema and schemas
-    WHEN constractable is called with the schema and schemas
+    WHEN constructable is called with the schema and schemas
     THEN the expected constructable is returned.
     """
     result = helpers.schema.constructable(schema=schema, schemas=schemas)
@@ -75,10 +75,10 @@ def test_constractable(schema, schemas, expected_result):
 
 
 @pytest.mark.helper
-def test_constractable_remote(tmp_path, _clean_remote_schemas_store):
+def test_constructable_remote(tmp_path, _clean_remote_schemas_store):
     """
     GIVEN schema with remote $ref with x-tablename
-    WHEN constractable is called with the schema
+    WHEN constructable is called with the schema
     THEN True is returned.
     """
     # Create file
diff --git a/tests/open_alchemy/test_build.py b/tests/open_alchemy/test_build.py
index 7490e196..71f5c8d6 100644
--- a/tests/open_alchemy/test_build.py
+++ b/tests/open_alchemy/test_build.py
@@ -1,4 +1,5 @@
 """Tests for the package builder."""
+
 import pytest
 
 from open_alchemy import build
@@ -510,6 +511,9 @@ def test_calculate_version(spec, spec_str, expected_version):
         ),
     ],
 )
+# @pytest.mark.xfail(
+#     condition=sys.platform == "win32", reason="feature not supported on Windows"
+# )
 @pytest.mark.build
 def test_execute(tmp_path, package_format, extensions):
     """
@@ -612,6 +616,9 @@ init_json(parent_path / "spec.json")"""
         assert len(files) == 1
 
 
+# @pytest.mark.xfail(
+#     condition=sys.platform == "win32", reason="feature not supported on Windows"
+# )
 @pytest.mark.integration
 def test_build_dist_wheel_import_error(tmp_path):
     """
@@ -640,13 +647,13 @@ def test_build_dist_wheel_import_error(tmp_path):
     }
 
     try:
-        build.run("pip uninstall -y wheel", ".")
+        build.run(["pip", "uninstall", "-y", "wheel"], ".")
         with pytest.raises(RuntimeError):
             build.execute(
                 spec=spec, name=name, path=str(dist), format_=build.PackageFormat.WHEEL
             )
     finally:
-        build.run("pip install wheel", ".")
+        build.run(["pip", "install", "wheel"], ".")
 
 
 @pytest.mark.parametrize(
@@ -657,7 +664,7 @@ def test_build_dist_wheel_import_error(tmp_path):
         pytest.param(build.PackageFormat.WHEEL, id="build a wheel archive"),
         pytest.param(
             build.PackageFormat.SDIST | build.PackageFormat.WHEEL,
-            id="build a sdist and a whell archive",
+            id="build a sdist and a wheel archive",
         ),
     ],
 )