Skip to content

Commit 716511a

Browse files
Merge pull request #446 from henryiii/henryiii/win27
Python 2.7 fixes
2 parents 0dc3268 + c17e67f commit 716511a

File tree

6 files changed

+232
-7
lines changed

6 files changed

+232
-7
lines changed

.github/workflows/python-tests.yml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ jobs:
1818
python_version: [ '2.7', '3.5', '3.6', '3.7', '3.8', 'pypy2', 'pypy3' ]
1919
os: [windows-latest, ubuntu-latest] #, macos-latest]
2020
exclude:
21-
- os: windows-latest
22-
python_version: "2.7"
2321
- os: windows-latest
2422
python_version: "pypy2"
2523
include:
@@ -61,7 +59,7 @@ jobs:
6159
architecture: x64
6260
# self install testing needs some clarity
6361
# so its being executed without any other tools running
64-
- run: pip install -U setuptools jaraco.windows
62+
- run: pip install -U setuptools
6563
- run: python setup.py egg_info
6664
- run: python setup.py sdist
6765
- run: easy_install dist/*
@@ -93,7 +91,7 @@ jobs:
9391
- name: Install dependencies
9492
run: |
9593
python -m pip install --upgrade pip
96-
pip install --upgrade wheel setuptools jaraco.windows
94+
pip install --upgrade wheel setuptools
9795
- run: python setup.py egg_info
9896
- name: Build package
9997
run: python setup.py bdist_egg
@@ -115,7 +113,7 @@ jobs:
115113
- name: Install dependencies
116114
run: |
117115
python -m pip install --upgrade pip
118-
pip install --upgrade wheel setuptools jaraco.windows
116+
pip install --upgrade wheel setuptools
119117
- run: python setup.py egg_info
120118
- name: Build package
121119
run: python setup.py bdist_wheel sdist

setup.cfg

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ zip_safe = true
3232
python_requires= >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*
3333
install_requires=
3434
setuptools
35-
jaraco.windows; python_version=="2.7"
3635
packages=find:
3736
package_dir=
3837
=src

src/setuptools_scm/git.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
import warnings
77

88

9-
from os.path import samefile
9+
try:
10+
from os.path import samefile
11+
except ImportError:
12+
from .win_py31_compat import samefile
1013

1114

1215
DEFAULT_DESCRIBE = "git describe --dirty --tags --long --match *[0-9]*"
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
"""
2+
Backport of os.path.samefile for Python prior to 3.2
3+
on Windows from jaraco.windows 3.8.
4+
5+
DON'T EDIT THIS FILE!
6+
7+
Instead, file tickets and PR's with `jaraco.windows
8+
<https://github.com/jaraco/jaraco.windows>`_ and request
9+
a port to setuptools_scm.
10+
"""
11+
12+
import os
13+
import nt
14+
import posixpath
15+
import ctypes.wintypes
16+
import sys
17+
import __builtin__ as builtins
18+
19+
20+
##
21+
# From jaraco.windows.error
22+
23+
def format_system_message(errno):
24+
"""
25+
Call FormatMessage with a system error number to retrieve
26+
the descriptive error message.
27+
"""
28+
# first some flags used by FormatMessageW
29+
ALLOCATE_BUFFER = 0x100
30+
FROM_SYSTEM = 0x1000
31+
32+
# Let FormatMessageW allocate the buffer (we'll free it below)
33+
# Also, let it know we want a system error message.
34+
flags = ALLOCATE_BUFFER | FROM_SYSTEM
35+
source = None
36+
message_id = errno
37+
language_id = 0
38+
result_buffer = ctypes.wintypes.LPWSTR()
39+
buffer_size = 0
40+
arguments = None
41+
bytes = ctypes.windll.kernel32.FormatMessageW(
42+
flags,
43+
source,
44+
message_id,
45+
language_id,
46+
ctypes.byref(result_buffer),
47+
buffer_size,
48+
arguments,
49+
)
50+
# note the following will cause an infinite loop if GetLastError
51+
# repeatedly returns an error that cannot be formatted, although
52+
# this should not happen.
53+
handle_nonzero_success(bytes)
54+
message = result_buffer.value
55+
ctypes.windll.kernel32.LocalFree(result_buffer)
56+
return message
57+
58+
59+
class WindowsError(builtins.WindowsError):
60+
"""
61+
More info about errors at
62+
http://msdn.microsoft.com/en-us/library/ms681381(VS.85).aspx
63+
"""
64+
65+
def __init__(self, value=None):
66+
if value is None:
67+
value = ctypes.windll.kernel32.GetLastError()
68+
strerror = format_system_message(value)
69+
if sys.version_info > (3, 3):
70+
args = 0, strerror, None, value
71+
else:
72+
args = value, strerror
73+
super(WindowsError, self).__init__(*args)
74+
75+
@property
76+
def message(self):
77+
return self.strerror
78+
79+
@property
80+
def code(self):
81+
return self.winerror
82+
83+
def __str__(self):
84+
return self.message
85+
86+
def __repr__(self):
87+
return '{self.__class__.__name__}({self.winerror})'.format(**vars())
88+
89+
90+
def handle_nonzero_success(result):
91+
if result == 0:
92+
raise WindowsError()
93+
94+
95+
##
96+
# From jaraco.windows.api.filesystem
97+
98+
FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000
99+
FILE_FLAG_BACKUP_SEMANTICS = 0x2000000
100+
OPEN_EXISTING = 3
101+
FILE_ATTRIBUTE_NORMAL = 0x80
102+
FILE_READ_ATTRIBUTES = 0x80
103+
INVALID_HANDLE_VALUE = ctypes.wintypes.HANDLE(-1).value
104+
105+
106+
class BY_HANDLE_FILE_INFORMATION(ctypes.Structure):
107+
_fields_ = [
108+
('file_attributes', ctypes.wintypes.DWORD),
109+
('creation_time', ctypes.wintypes.FILETIME),
110+
('last_access_time', ctypes.wintypes.FILETIME),
111+
('last_write_time', ctypes.wintypes.FILETIME),
112+
('volume_serial_number', ctypes.wintypes.DWORD),
113+
('file_size_high', ctypes.wintypes.DWORD),
114+
('file_size_low', ctypes.wintypes.DWORD),
115+
('number_of_links', ctypes.wintypes.DWORD),
116+
('file_index_high', ctypes.wintypes.DWORD),
117+
('file_index_low', ctypes.wintypes.DWORD),
118+
]
119+
120+
@property
121+
def file_size(self):
122+
return (self.file_size_high << 32) + self.file_size_low
123+
124+
@property
125+
def file_index(self):
126+
return (self.file_index_high << 32) + self.file_index_low
127+
128+
129+
class SECURITY_ATTRIBUTES(ctypes.Structure):
130+
_fields_ = (
131+
('length', ctypes.wintypes.DWORD),
132+
('p_security_descriptor', ctypes.wintypes.LPVOID),
133+
('inherit_handle', ctypes.wintypes.BOOLEAN),
134+
)
135+
136+
137+
LPSECURITY_ATTRIBUTES = ctypes.POINTER(SECURITY_ATTRIBUTES)
138+
139+
140+
CreateFile = ctypes.windll.kernel32.CreateFileW
141+
CreateFile.argtypes = (
142+
ctypes.wintypes.LPWSTR,
143+
ctypes.wintypes.DWORD,
144+
ctypes.wintypes.DWORD,
145+
LPSECURITY_ATTRIBUTES,
146+
ctypes.wintypes.DWORD,
147+
ctypes.wintypes.DWORD,
148+
ctypes.wintypes.HANDLE,
149+
)
150+
CreateFile.restype = ctypes.wintypes.HANDLE
151+
152+
GetFileInformationByHandle = ctypes.windll.kernel32.GetFileInformationByHandle
153+
GetFileInformationByHandle.restype = ctypes.wintypes.BOOL
154+
GetFileInformationByHandle.argtypes = (
155+
ctypes.wintypes.HANDLE,
156+
ctypes.POINTER(BY_HANDLE_FILE_INFORMATION),
157+
)
158+
159+
160+
##
161+
# From jaraco.windows.filesystem
162+
163+
def compat_stat(path):
164+
"""
165+
Generate stat as found on Python 3.2 and later.
166+
"""
167+
stat = os.stat(path)
168+
info = get_file_info(path)
169+
# rewrite st_ino, st_dev, and st_nlink based on file info
170+
return nt.stat_result(
171+
(stat.st_mode,) +
172+
(info.file_index, info.volume_serial_number, info.number_of_links) +
173+
stat[4:]
174+
)
175+
176+
177+
def samefile(f1, f2):
178+
"""
179+
Backport of samefile from Python 3.2 with support for Windows.
180+
"""
181+
return posixpath.samestat(compat_stat(f1), compat_stat(f2))
182+
183+
184+
def get_file_info(path):
185+
# open the file the same way CPython does in posixmodule.c
186+
desired_access = FILE_READ_ATTRIBUTES
187+
share_mode = 0
188+
security_attributes = None
189+
creation_disposition = OPEN_EXISTING
190+
flags_and_attributes = (
191+
FILE_ATTRIBUTE_NORMAL |
192+
FILE_FLAG_BACKUP_SEMANTICS |
193+
FILE_FLAG_OPEN_REPARSE_POINT
194+
)
195+
template_file = None
196+
197+
handle = CreateFile(
198+
path,
199+
desired_access,
200+
share_mode,
201+
security_attributes,
202+
creation_disposition,
203+
flags_and_attributes,
204+
template_file,
205+
)
206+
207+
if handle == INVALID_HANDLE_VALUE:
208+
raise WindowsError()
209+
210+
info = BY_HANDLE_FILE_INFORMATION()
211+
res = GetFileInformationByHandle(handle, info)
212+
handle_nonzero_success(res)
213+
214+
return info

testing/test_file_finder.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
@pytest.fixture(params=["git", "hg"])
1010
def inwd(request, wd, monkeypatch):
1111
if request.param == "git":
12+
if sys.platform == "win32" and sys.version_info[0] < 3:
13+
pytest.skip("Long/short path names supported on Windows Python 2.7")
1214
try:
1315
wd("git init")
1416
except OSError:

testing/test_git.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@
1010
import warnings
1111

1212

13+
skip_if_win_27 = pytest.mark.skipif(
14+
sys.platform == "win32" and sys.version_info[0] < 3,
15+
reason="Not supported on Windows + Python 2.7",
16+
)
17+
18+
1319
with warnings.catch_warnings():
1420
warnings.filterwarnings("ignore")
1521
if not has_command("git"):
@@ -186,6 +192,7 @@ def test_alphanumeric_tags_match(wd):
186192
assert wd.version.startswith("0.1.dev1+g")
187193

188194

195+
@skip_if_win_27
189196
def test_git_archive_export_ignore(wd, monkeypatch):
190197
wd.write("test1.txt", "test")
191198
wd.write("test2.txt", "test")
@@ -201,6 +208,7 @@ def test_git_archive_export_ignore(wd, monkeypatch):
201208
assert integration.find_files(".") == [opj(".", "test1.txt")]
202209

203210

211+
@skip_if_win_27
204212
@pytest.mark.issue(228)
205213
def test_git_archive_subdirectory(wd, monkeypatch):
206214
wd("mkdir foobar")
@@ -211,6 +219,7 @@ def test_git_archive_subdirectory(wd, monkeypatch):
211219
assert integration.find_files(".") == [opj(".", "foobar", "test1.txt")]
212220

213221

222+
@skip_if_win_27
214223
@pytest.mark.issue(251)
215224
def test_git_archive_run_from_subdirectory(wd, monkeypatch):
216225
wd("mkdir foobar")

0 commit comments

Comments
 (0)