Repository: emc-isilon/pike (language: python)

Issue:
document py3c-dev requirement for krb build



Discussion / hints:

Fix patch:
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 00000000..2969c9f6
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,14 @@
+__pycache__
+.coverage
+.coverage.*
+.idea
+.pytest_cache
+.tox
+
+*.pyc
+*.egg-info/
+
+build
+dist
+doc/source/stubs
+wheelhouse
\ No newline at end of file
diff --git a/.github/workflows/publishpackage.yml b/.github/workflows/publishpackage.yml
deleted file mode 100644
index 3aa513c8..00000000
--- a/.github/workflows/publishpackage.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (c) 2020, Dell Inc. or its subsidiaries.
-# All rights reserved.
-# See file LICENSE for licensing information.
----
-name: Upload Python Package
-
-on:
-  release:
-    types: [created]
-
-jobs:
-  deploy:
-    runs-on: ubuntu-latest
-    steps:
-    - uses: actions/checkout@v1
-    - name: Set up Python
-      uses: actions/setup-python@v1
-      with:
-        python-version: '3.x'
-    - name: Install dependencies
-      run: |
-        python -m pip install --upgrade pip
-        pip install setuptools wheel twine
-    - name: Build and publish
-      env:
-        TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
-        TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
-        TWINE_REPOSITORY_URL: ${{ secrets.PYPI_INDEX }}
-      run: |
-        python setup.py sdist bdist_wheel --universal
-        twine upload dist/*
diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml
new file mode 100644
index 00000000..381e1bda
--- /dev/null
+++ b/.github/workflows/wheels.yml
@@ -0,0 +1,61 @@
+name: Build and Publish Wheels
+
+on: [push, pull_request]
+
+jobs:
+  build_wheels:
+    name: Build py3 wheels on ubuntu-22.04
+    runs-on: ubuntu-22.04
+
+    steps:
+      - uses: actions/checkout@v3
+      - uses: actions/setup-python@v3
+      - name: Install cibuildwheel
+        run: python -m pip install cibuildwheel==2.11.3
+      - name: Build wheels
+        run: ./samba/dc.sh ./build_and_test_wheels.sh
+      - uses: actions/upload-artifact@v3
+        with:
+          path: ./wheelhouse/*.whl
+
+  build_wheels_py27:
+    name: Build py27 wheel on ubuntu-22.04
+    runs-on: ubuntu-22.04
+
+    steps:
+      - uses: actions/checkout@v3
+      - uses: actions/setup-python@v3
+      - name: Build wheels
+        run: ./samba/dc.sh ./build_and_test_wheels27.sh
+      - uses: actions/upload-artifact@v3
+        with:
+          path: ./wheelhouse/*.whl
+
+  build_sdist:
+    name: Build source distribution
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+
+      - name: Build sdist
+        run: pipx run build --sdist
+
+      - uses: actions/upload-artifact@v3
+        with:
+          path: dist/*.tar.gz
+
+  upload_pypi:
+    needs: [build_wheels, build_wheels_py27, build_sdist]
+    runs-on: ubuntu-latest
+    # upload to PyPI on every tag starting with 'v'
+    if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
+    steps:
+      - uses: actions/download-artifact@v3
+        with:
+          name: artifact
+          path: dist
+
+      - uses: pypa/gh-action-pypi-publish@v1.5.0
+        with:
+          user: ${{ secrets.PYPI_USERNAME }}
+          password: ${{ secrets.PYPI_PASSWORD }}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 202dc25f..2969c9f6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,14 @@
+__pycache__
+.coverage
+.coverage.*
+.idea
+.pytest_cache
+.tox
+
 *.pyc
+*.egg-info/
+
 build
 dist
-Pike.egg-info/
 doc/source/stubs
+wheelhouse
\ No newline at end of file
diff --git a/.gitignorerevs b/.gitignorerevs
index 17008820..9160f2dd 100644
--- a/.gitignorerevs
+++ b/.gitignorerevs
@@ -1,3 +1,5 @@
 # blackify
 03f70b27c00a188790c40a0a59e4cbfce29b9719
 e347aeb25405a29e43492c125e532c969aa6d6c7
+# move to /src
+384b32c2436a974d8260b65c663567e669d5b2d5
diff --git a/build_all_wheels.sh b/build_all_wheels.sh
deleted file mode 100755
index 53f9931a..00000000
--- a/build_all_wheels.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/bin/env bash
-
-# build compiled wheel versions of pike with multiple different python versions
-
-# ensure system build requirements are met (see README.md)
-
-# recommend the use of `pyenv` to install and activate
-# multiple python versions before running the script.
-
-set -euo pipefail
-
-versions=(python2 python3.6 python3.7 python3.8 python3.9 python3.10)
-
-rm -rf dist
-for v in ${versions[@]}
-do
-    $v -m pip install --user 'build[virtualenv]' && $v -m build
-done
-
-cat <<EOF
-To upload packages run:
-python3 -m pip install --user twine
-python3 -m twine upload dist/* --repository-url "<my-private-repo>"'
-EOF
diff --git a/buildwheel/Dockerfile b/buildwheel/Dockerfile
new file mode 100644
index 00000000..24efe85a
--- /dev/null
+++ b/buildwheel/Dockerfile
@@ -0,0 +1,8 @@
+FROM quay.io/pypa/manylinux2010_x86_64
+COPY . /src/pike
+RUN yum install -y krb5-devel krb5-workstation python27 && \
+    source /opt/rh/python27/enable && \
+    python2.7 -m pip install -U 'pip<21' && \
+    python2.7 -m pip install build
+
+CMD /src/pike/buildwheel/wheel27.sh
diff --git a/buildwheel/README.md b/buildwheel/README.md
new file mode 100644
index 00000000..654a4198
--- /dev/null
+++ b/buildwheel/README.md
@@ -0,0 +1,8 @@
+# buildwheel
+
+build pike-smb2 manylinux wheel (including for python2.7)
+
+```
+docker build -t pike-buildwheel buildwheel
+docker run -it -v /tmp/wheelhouse:/src/pike/wheelhouse pike-buildwheel
+```
diff --git a/buildwheel/wheel27.sh b/buildwheel/wheel27.sh
new file mode 100755
index 00000000..f945ab35
--- /dev/null
+++ b/buildwheel/wheel27.sh
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+# source before bash "strict mode"
+source /opt/rh/python27/enable
+
+set -euxo pipefail
+
+# build py27 wheel
+cd /src/pike
+python2.7 -m build
+auditwheel repair dist/*.whl
+
+# Test the wheel
+export PIKE_SERVER=$SAMBA_SERVER
+export PIKE_SHARE=s1
+
+./samba/krb5.conf.sh
+python2.7 -m pip install wheelhouse/*-cp27*.whl
+python2.7 -m unittest pike.test.session
+
+# if the wheelhouse dir is mounted, copy tested wheels to host
+[[ -d /wheelhouse ]] && cp wheelhouse/* /wheelhouse/
diff --git a/pyproject.toml b/pyproject.toml
index eec797bf..867d1a52 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,5 +1,24 @@
 [build-system]
-requires = ["setuptools>=30.3.0", "wheel", "setuptools_scm[toml]"]
+requires = ["setuptools>=44", "wheel", "setuptools_scm[toml]"]
 build-backend = "setuptools.build_meta"
 
 [tool.setuptools_scm]
+
+[tool.cibuildwheel]
+# skip x86 pypi since pycryptodome packages
+# are broken on these playforms (at least py37)
+skip = ["pp*-manylinux_i686"]
+before-test = ["{project}/samba/krb5.conf.sh"]
+test-command = [
+  "export PIKE_SERVER=$SAMBA_SERVER",
+  "export PIKE_SHARE=s1",
+  "python -m unittest pike.test.session",
+]
+
+[tool.cibuildwheel.linux]
+environment-pass = ["HOST_IPV4", "SAMBA_REALM", "SAMBA_SERVER", "ADMIN_PASSWORD"]
+before-all = "yum install -y krb5-devel krb5-workstation"
+
+[[tool.cibuildwheel.overrides]]
+select = "*-musllinux*"
+before-all = "apk add krb5 krb5-dev"
diff --git a/samba/.gitignore b/samba/.gitignore
new file mode 100644
index 00000000..007e42e2
--- /dev/null
+++ b/samba/.gitignore
@@ -0,0 +1,2 @@
+s1
+krb5.conf
diff --git a/samba/Dockerfile b/samba/Dockerfile
new file mode 100644
index 00000000..06f8c5a6
--- /dev/null
+++ b/samba/Dockerfile
@@ -0,0 +1,5 @@
+FROM debian:11
+
+RUN apt-get update && \
+    DEBIAN_FRONTEND=noninteractive \
+    apt-get install -y krb5-user libkrb5-dev python2-dev python3-dev build-essential python3-pip git
diff --git a/samba/README.md b/samba/README.md
new file mode 100644
index 00000000..bc5a414c
--- /dev/null
+++ b/samba/README.md
@@ -0,0 +1,29 @@
+# Samba DC environment
+
+This environment exists for running tests using kerberos authentication.
+
+## Requirements
+
+The host requires `docker compose` installed and functional.
+
+## Run All Tests
+
+Using kerberos authentication:
+
+```shell
+./samba/dc.sh docker compose up runner
+```
+
+## Build Wheels
+
+Must run on linux; will use docker containers for the build.
+
+```shell
+pip install --user cibuildwheel
+./samba/dc.sh ./build_and_test_wheels.sh
+```
+
+After each wheel is built, it will be installed and used to establish a kerberos
+session to the Samba DC.
+
+Built wheels will be placed in pike/wheelhouse.
\ No newline at end of file
diff --git a/samba/admin_password b/samba/admin_password
new file mode 100644
index 00000000..7973ca92
--- /dev/null
+++ b/samba/admin_password
@@ -0,0 +1,1 @@
+Pik3IzC00lss
diff --git a/samba/dc.sh b/samba/dc.sh
new file mode 100755
index 00000000..dce9be51
--- /dev/null
+++ b/samba/dc.sh
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+
+set -euxo pipefail
+
+SCRIPT=$(readlink -f "$0")
+SCRIPTPATH=$(dirname "$SCRIPT")
+
+IF=$(/sbin/ip route|awk '/default/ { print $5 }')
+export HOST_IPV4=$(ip -4 addr show $IF | grep -Po 'inet \K[\d.]+')
+export SAMBADC_INTERFACES=$IF
+export SAMBADC_HOSTNAME=dc
+export SAMBA_WORKGROUP=PIKE
+export SAMBA_REALM=${SAMBA_WORKGROUP}.TEST.LOCAL
+export SAMBA_SERVER=${SAMBADC_HOSTNAME}.${SAMBA_REALM}
+export ADMIN_PASSWORD=$(cat "$SCRIPTPATH/admin_password")
+
+function cleanup ()
+{
+    docker compose down
+    docker volume rm samba_lib samba_etc
+}
+
+trap cleanup EXIT
+cd "$SCRIPTPATH"
+docker compose up dc &
+sleep 30
+# pass off control to remaining args
+"$@"
diff --git a/samba/docker-compose.yml b/samba/docker-compose.yml
new file mode 100644
index 00000000..95d37d47
--- /dev/null
+++ b/samba/docker-compose.yml
@@ -0,0 +1,54 @@
+version: "3.1"
+
+services:
+  runner:
+    build: .
+    environment:
+      - HOST_IPV4
+      - SAMBA_REALM
+      - SAMBA_SERVER
+      - ADMIN_PASSWORD
+    command: /src/pike/samba/run_all.sh
+    volumes:
+      - ${PWD}/..:/src/pike
+  dc:
+    image: ${REGISTRY_URI:-instantlinux}/samba-dc:latest
+    restart: always
+    network_mode: host
+    cap_add:
+      - CAP_SYS_ADMIN
+    hostname: ${SAMBADC_HOSTNAME:-dc}
+    environment:
+      INTERFACES: ${SAMBADC_INTERFACES:-lo eth0}
+      REALM: ${SAMBA_REALM:-pike.local}
+      TZ: ${TZ:-UTC}
+      WORKGROUP: ${SAMBA_WORKGROUP:-PIKE}
+    volumes:
+      - etc:/etc/samba
+      - lib:/var/lib/samba
+      - ${PWD}/share.conf:/etc/samba/conf.d/share.conf
+      - ${PWD}/samba/s1:/srv/s1
+    ports:
+      - 53:53
+      - 53:53/udp
+      - 88:88
+      - 88:88/udp
+      - 135:135
+      - 137-138:137-138/udp
+      - 139:139
+      - 389:389
+      - 389:389/udp
+      - 445:445
+      - 464:464
+      - 464:464/udp
+      - 636:636
+      - 3268-3269:3268-3269
+      - 49152-65535
+    secrets:
+      - samba-admin-password
+volumes:
+  etc:
+  lib:
+secrets:
+  samba-admin-password:
+    file: ${PWD}/admin_password
diff --git a/samba/krb5.conf.sh b/samba/krb5.conf.sh
new file mode 100755
index 00000000..47af9e73
--- /dev/null
+++ b/samba/krb5.conf.sh
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+set -euxo pipefail
+
+cat - >> /etc/hosts <<EOF
+$HOST_IPV4  $SAMBA_REALM
+$HOST_IPV4  $SAMBA_SERVER
+EOF
+
+cat - > /etc/krb5.conf <<EOF
+[libdefaults]
+  default_realm = $SAMBA_REALM
+
+[realms]
+$SAMBA_REALM = {
+  kdc = $SAMBA_SERVER
+}
+EOF
+
+cat /etc/hosts /etc/krb5.conf
+
+printf "$ADMIN_PASSWORD\n" | kinit administrator
diff --git a/samba/run_all.sh b/samba/run_all.sh
new file mode 100755
index 00000000..460ac580
--- /dev/null
+++ b/samba/run_all.sh
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+
+# run this inside of a container to kick off the tests
+
+set -euxo pipefail
+
+SCRIPT=$(readlink -f "$0")
+SCRIPTPATH=$(dirname "$SCRIPT")
+
+$SCRIPTPATH/krb5.conf.sh
+
+export PIKE_SERVER=$SAMBA_SERVER
+export PIKE_SHARE=s1
+python3 -m pip install /src/pike
+python3 -m unittest pike.test.samba_suite
+pytest -ra
diff --git a/samba/share.conf b/samba/share.conf
new file mode 100644
index 00000000..e85ff7bc
--- /dev/null
+++ b/samba/share.conf
@@ -0,0 +1,4 @@
+[s1]
+	path = /srv/s1
+	read only = no
+
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 00000000..25c5ab14
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,53 @@
+[metadata]
+name = pike-smb2
+description = Pure python SMB client
+long_description = file: README.md, LICENSE
+long_description_content_type = text/markdown
+author = Brian Koropoff
+author_email = Brian.Koropoff@emc.com
+maintainer = Masen Furer
+maintainer_email = m_github@0x26.net
+license = Simplified BSD License
+url = https://github.com/emc-isilon/pike
+project_urls =
+    Source = https://github.com/emc-isilon/pike
+    Bug Reports = https://github.com/emc-isilon/pike/issues
+platforms = unix, linux, osx, cygwin, win32
+keywords = smb, smb-testing, smb-client
+classifiers = 
+    Development Status :: 4 - Beta
+    Intended Audience :: Developers
+    Topic :: Software Development :: Testing
+    Programming Language :: Python
+    Programming Language :: Python :: 2
+    Programming Language :: Python :: 2.7
+    Programming Language :: Python :: 3
+    Programming Language :: Python :: 3.6
+    Programming Language :: Python :: 3.7
+    Programming Language :: Python :: 3.8
+    Programming Language :: Python :: 3.9
+    Programming Language :: Python :: 3.10
+    Programming Language :: Python :: Implementation :: CPython
+    Operating System :: OS Independent
+    Environment :: Console
+    License :: OSI Approved :: BSD License
+
+[options]
+install_requires =
+    enum34~=1.1.6;  python_version ~= "2.7"
+    pathlib2~=2.3.5;  python_version ~= "2.7"
+    attrs >= 19.3
+    future
+    pycryptodome
+    six
+python_requires = >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*
+package_dir=
+    =src
+packages = find:
+
+[options.packages.find]
+where=src
+
+[options.entry_points]
+pytest11 = 
+    pike = pike.pytest_support
diff --git a/setup.py b/setup.py
index 519b3fa8..48846684 100755
--- a/setup.py
+++ b/setup.py
@@ -4,53 +4,16 @@
 # All rights reserved.
 # See file LICENSE for licensing information.
 #
-
 import ctypes
 import os
-import unittest
 
 try:
-    from setuptools import setup, Extension, Command
+    from setuptools import setup, Extension
 except ImportError:
-    from distutils.core import setup, Extension, Command
-from distutils.command.build_ext import build_ext
-from distutils.command.build_py import build_py
+    from distutils.core import setup, Extension
 from distutils.errors import CCompilerError, DistutilsExecError, DistutilsPlatformError
 
 _HERED = os.path.abspath(os.path.dirname(__file__))
-_README = os.path.join(_HERED, "README.md")
-
-# attempt building the kerberos extension
-try_krb = True
-ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError)
-
-
-class BuildFailed(Exception):
-    pass
-
-
-class ve_build_ext(build_ext):
-    # This class allows C extension building to fail.
-
-    def run(self):
-        try:
-            build_ext.run(self)
-        except DistutilsPlatformError:
-            raise BuildFailed()
-
-    def build_extension(self, ext):
-        try:
-            build_ext.build_extension(self, ext)
-        except ext_errors:
-            raise BuildFailed()
-
-
-class ve_build_py(build_py):
-    def run(self, *args, **kwds):
-        if not try_krb:
-            print("libgssapi_krb5 not available, skipping kerberos module")
-        build_py.run(self, *args, **kwds)
-
 
 try:
     libgssapi_krb5 = ctypes.CDLL("libgssapi_krb5.so")
@@ -74,83 +37,7 @@ def run(self, *args, **kwds):
         libraries=["gssapi_krb5"],
         define_macros=defines,
     )
-except OSError:
-    try_krb = False
-
-
-def pike_suite():
-    return unittest.defaultTestLoader.discover("pike/test", pattern="*.py")
-
-
-# Get the long description from the README.md file
-with open(_README, "rb") as f_:
-    long_description = f_.read().decode("utf-8")
-
-
-def run_setup(with_extensions):
-    ext_modules = []
-    cmdclass = {"build_py": ve_build_py}
-    if with_extensions:
-        ext_modules.append(lw_krb_module)
-        cmdclass = dict(cmdclass, build_ext=ve_build_ext)
-    setup(
-        name="pike-smb2",
-        use_scm_version=True,
-        description="Pure python SMB client",
-        long_description_content_type="text/markdown",
-        long_description=long_description,
-        author="Brian Koropoff",
-        author_email="Brian.Koropoff@emc.com",
-        maintainer="Masen Furer",
-        maintainer_email="Masen.Furer@dell.com",
-        url="https://github.com/emc-isilon/pike",
-        project_urls={
-            "Source": "https://github.com/emc-isilon/pike",
-            "Bug Reports": "https://github.com/emc-isilon/pike/issues",
-        },
-        license="Simplified BSD License",
-        packages=["pike", "pike.test"],
-        entry_points={
-            "pytest11": [
-                "pike = pike.pytest_support",
-            ]
-        },
-        python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*",
-        install_requires=[
-            'enum34~=1.1.6;  python_version ~= "2.7"',
-            "attrs >= 19.3",
-            'pathlib2~=2.3.5;  python_version ~= "2.7"',
-            "pycryptodome",
-            "future",
-            "six",
-        ],
-        ext_modules=ext_modules,
-        test_suite="setup.pike_suite",
-        cmdclass=cmdclass,
-        # see https://pypi.org/classifiers/
-        classifiers=[
-            "Development Status :: 4 - Beta",
-            "Intended Audience :: Developers",
-            "Topic :: Software Development :: Testing",
-            "Programming Language :: Python",
-            "Programming Language :: Python :: 2",
-            "Programming Language :: Python :: 2.7",
-            "Programming Language :: Python :: 3",
-            "Programming Language :: Python :: 3.6",
-            "Programming Language :: Python :: 3.7",
-            "Programming Language :: Python :: 3.8",
-            "Programming Language :: Python :: 3.9",
-            "Programming Language :: Python :: 3.10",
-            "Programming Language :: Python :: Implementation :: CPython",
-            "Operating System :: OS Independent",
-            "Environment :: Console",
-            "License :: OSI Approved :: BSD License",
-        ],
-        keywords="smb smb-testing smb-client",
-    )
-
-
-try:
-    run_setup(with_extensions=try_krb)
-except BuildFailed:
-    run_setup(with_extensions=False)
+    setup(ext_modules=[lw_krb_module])
+except (OSError, CCompilerError, DistutilsExecError, DistutilsPlatformError):
+    print("libgssapi_krb5 not available, skipping kerberos module")
+    setup()
diff --git a/pike/__init__.py b/src/pike/__init__.py
similarity index 100%
rename from pike/__init__.py
rename to src/pike/__init__.py
diff --git a/pike/auth.py b/src/pike/auth.py
similarity index 100%
rename from pike/auth.py
rename to src/pike/auth.py
diff --git a/pike/core.py b/src/pike/core.py
similarity index 100%
rename from pike/core.py
rename to src/pike/core.py
diff --git a/pike/crypto.py b/src/pike/crypto.py
similarity index 100%
rename from pike/crypto.py
rename to src/pike/crypto.py
diff --git a/pike/digest.py b/src/pike/digest.py
similarity index 100%
rename from pike/digest.py
rename to src/pike/digest.py
diff --git a/pike/exceptions.py b/src/pike/exceptions.py
similarity index 100%
rename from pike/exceptions.py
rename to src/pike/exceptions.py
diff --git a/pike/io.py b/src/pike/io.py
similarity index 100%
rename from pike/io.py
rename to src/pike/io.py
diff --git a/pike/model.py b/src/pike/model.py
similarity index 99%
rename from pike/model.py
rename to src/pike/model.py
index ae4da790..f5fa4693 100644
--- a/pike/model.py
+++ b/src/pike/model.py
@@ -1313,7 +1313,6 @@ def addchannel(self, conn, signing_key):
         return channel
 
     def delchannel(self, conn):
-        del conn._sessions[self.session_id]
         del self._channels[id(conn)]
 
     def first_channel(self):
diff --git a/pike/netbios.py b/src/pike/netbios.py
similarity index 100%
rename from pike/netbios.py
rename to src/pike/netbios.py
diff --git a/pike/ntlm.py b/src/pike/ntlm.py
similarity index 100%
rename from pike/ntlm.py
rename to src/pike/ntlm.py
diff --git a/pike/ntstatus.py b/src/pike/ntstatus.py
similarity index 100%
rename from pike/ntstatus.py
rename to src/pike/ntstatus.py
diff --git a/pike/nttime.py b/src/pike/nttime.py
similarity index 100%
rename from pike/nttime.py
rename to src/pike/nttime.py
diff --git a/pike/path.py b/src/pike/path.py
similarity index 100%
rename from pike/path.py
rename to src/pike/path.py
diff --git a/pike/smb2.py b/src/pike/smb2.py
similarity index 100%
rename from pike/smb2.py
rename to src/pike/smb2.py
diff --git a/pike/transport.py b/src/pike/transport.py
similarity index 100%
rename from pike/transport.py
rename to src/pike/transport.py


Test patch:
diff --git a/samba/build_and_test_wheels.sh b/samba/build_and_test_wheels.sh
new file mode 100755
index 00000000..96b9fd5f
--- /dev/null
+++ b/samba/build_and_test_wheels.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+set -euxo pipefail
+
+SCRIPT=$(readlink -f "$0")
+SCRIPTPATH=$(dirname "$SCRIPT")
+cd $SCRIPTPATH/..
+
+python3 -m cibuildwheel --platform linux "$@"
\ No newline at end of file
diff --git a/samba/build_and_test_wheels27.sh b/samba/build_and_test_wheels27.sh
new file mode 100755
index 00000000..ea053443
--- /dev/null
+++ b/samba/build_and_test_wheels27.sh
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+
+set -euxo pipefail
+
+SCRIPT=$(readlink -f "$0")
+SCRIPTPATH=$(dirname "$SCRIPT")
+cd $SCRIPTPATH/..
+
+docker build -t pike-buildwheel -f buildwheel/Dockerfile .
+docker run -e HOST_IPV4 -e SAMBA_REALM -e SAMBA_SERVER -e ADMIN_PASSWORD \
+       -v $SCRIPTPATH/../wheelhouse:/wheelhouse pike-buildwheel
diff --git a/pike/pytest_support.py b/src/pike/pytest_support.py
similarity index 100%
rename from pike/pytest_support.py
rename to src/pike/pytest_support.py
diff --git a/pike/test/__init__.py b/src/pike/test/__init__.py
similarity index 99%
rename from pike/test/__init__.py
rename to src/pike/test/__init__.py
index c68f4fbf..bc7f7077 100644
--- a/pike/test/__init__.py
+++ b/src/pike/test/__init__.py
@@ -375,7 +375,10 @@ def close(self):
         If the tree has already been disconnected or channel already closed, then
         errors related to cleaning up twice are suppressed.
         """
-        if not self.conn or not self.conn.connected:
+        session = None
+        if self.chan and self.conn:
+            session = self.conn.session(self.chan.session.session_id)
+        if not self.conn or not self.conn.connected or not session:
             self.conn = self.chan = self.tree = None
             return
         if self.tree and self.chan:
diff --git a/pike/test/acl.py b/src/pike/test/acl.py
similarity index 100%
rename from pike/test/acl.py
rename to src/pike/test/acl.py
diff --git a/pike/test/appinstanceid.py b/src/pike/test/appinstanceid.py
similarity index 100%
rename from pike/test/appinstanceid.py
rename to src/pike/test/appinstanceid.py
diff --git a/pike/test/changenotify.py b/src/pike/test/changenotify.py
similarity index 100%
rename from pike/test/changenotify.py
rename to src/pike/test/changenotify.py
diff --git a/pike/test/compound.py b/src/pike/test/compound.py
similarity index 100%
rename from pike/test/compound.py
rename to src/pike/test/compound.py
diff --git a/pike/test/copychunk.py b/src/pike/test/copychunk.py
similarity index 100%
rename from pike/test/copychunk.py
rename to src/pike/test/copychunk.py
diff --git a/pike/test/credit.py b/src/pike/test/credit.py
similarity index 100%
rename from pike/test/credit.py
rename to src/pike/test/credit.py
diff --git a/pike/test/durable.py b/src/pike/test/durable.py
similarity index 100%
rename from pike/test/durable.py
rename to src/pike/test/durable.py
diff --git a/pike/test/echo.py b/src/pike/test/echo.py
similarity index 100%
rename from pike/test/echo.py
rename to src/pike/test/echo.py
diff --git a/pike/test/encryption.py b/src/pike/test/encryption.py
similarity index 100%
rename from pike/test/encryption.py
rename to src/pike/test/encryption.py
diff --git a/pike/test/invalid_session.py b/src/pike/test/invalid_session.py
similarity index 100%
rename from pike/test/invalid_session.py
rename to src/pike/test/invalid_session.py
diff --git a/pike/test/invalid_tree.py b/src/pike/test/invalid_tree.py
similarity index 100%
rename from pike/test/invalid_tree.py
rename to src/pike/test/invalid_tree.py
diff --git a/pike/test/ioctl.py b/src/pike/test/ioctl.py
similarity index 100%
rename from pike/test/ioctl.py
rename to src/pike/test/ioctl.py
diff --git a/pike/test/lease.py b/src/pike/test/lease.py
similarity index 100%
rename from pike/test/lease.py
rename to src/pike/test/lease.py
diff --git a/pike/test/lock.py b/src/pike/test/lock.py
similarity index 100%
rename from pike/test/lock.py
rename to src/pike/test/lock.py
diff --git a/pike/test/multichannel.py b/src/pike/test/multichannel.py
similarity index 100%
rename from pike/test/multichannel.py
rename to src/pike/test/multichannel.py
diff --git a/pike/test/negotiate.py b/src/pike/test/negotiate.py
similarity index 100%
rename from pike/test/negotiate.py
rename to src/pike/test/negotiate.py
diff --git a/pike/test/oplock.py b/src/pike/test/oplock.py
similarity index 100%
rename from pike/test/oplock.py
rename to src/pike/test/oplock.py
diff --git a/pike/test/persistent.py b/src/pike/test/persistent.py
similarity index 100%
rename from pike/test/persistent.py
rename to src/pike/test/persistent.py
diff --git a/pike/test/pytest.ini b/src/pike/test/pytest.ini
similarity index 100%
rename from pike/test/pytest.ini
rename to src/pike/test/pytest.ini
diff --git a/pike/test/query.py b/src/pike/test/query.py
similarity index 100%
rename from pike/test/query.py
rename to src/pike/test/query.py
diff --git a/pike/test/querydirectory.py b/src/pike/test/querydirectory.py
similarity index 100%
rename from pike/test/querydirectory.py
rename to src/pike/test/querydirectory.py
diff --git a/pike/test/queryondiskid.py b/src/pike/test/queryondiskid.py
similarity index 100%
rename from pike/test/queryondiskid.py
rename to src/pike/test/queryondiskid.py
diff --git a/pike/test/readwrite.py b/src/pike/test/readwrite.py
similarity index 100%
rename from pike/test/readwrite.py
rename to src/pike/test/readwrite.py
diff --git a/pike/test/reparse.py b/src/pike/test/reparse.py
similarity index 100%
rename from pike/test/reparse.py
rename to src/pike/test/reparse.py
diff --git a/pike/test/session.py b/src/pike/test/session.py
similarity index 100%
rename from pike/test/session.py
rename to src/pike/test/session.py
diff --git a/pike/test/set.py b/src/pike/test/set.py
similarity index 100%
rename from pike/test/set.py
rename to src/pike/test/set.py
diff --git a/pike/test/tree.py b/src/pike/test/tree.py
similarity index 100%
rename from pike/test/tree.py
rename to src/pike/test/tree.py