Skip to content

Commit 4cd90b9

Browse files
committed
1 parent 81f4dfb commit 4cd90b9

File tree

4 files changed

+28
-51
lines changed

4 files changed

+28
-51
lines changed

README.rst

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,10 @@ Features that distinguish it from other similar libraries (see alternatives_).
2727
controlled with the ``overwrite`` parameter.
2828

2929
- Windows support, although untested. The MSDN resources are not very explicit
30-
about which operations are atomic. This requires ``pywin32``.
30+
about which operations are atomic.
3131

3232
- Simple high-level API that wraps a very flexible class-based API.
3333

34-
- Consistent error handling across platforms.
35-
3634

3735
How it works
3836
============
@@ -43,7 +41,7 @@ that the temporary file resides on the same filesystem.
4341
The temporary file will then be atomically moved to the target location: On
4442
POSIX, it will use ``rename`` if files should be overwritten, otherwise a
4543
combination of ``link`` and ``unlink``. On Windows, it uses ``MoveFileEx`` (see
46-
MSDN_) with the appropriate flags.
44+
MSDN_) through stdlib's ``ctypes`` with the appropriate flags.
4745

4846
Note that with ``link`` and ``unlink``, there's a timewindow where the file
4947
might be available under two entries in the filesystem: The name of the

atomicwrites/__init__.py

Lines changed: 21 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,57 +9,44 @@
99

1010
PY2 = sys.version_info[0] == 2
1111

12+
text_type = unicode if PY2 else str
1213

13-
class _FileExistsError(OSError if PY2 else FileExistsError):
14-
errno = errno.EEXIST
1514

16-
class _FileNotFoundError(OSError if PY2 else FileNotFoundError):
17-
errno = errno.ENOENT
15+
def _path_to_unicode(x):
16+
if not isinstance(x, text_type):
17+
return x.decode(sys.getfilesystemencoding())
18+
return x
1819

1920

2021
if sys.platform != 'win32':
21-
@contextlib.contextmanager
22-
def handle_errors():
23-
yield
24-
2522
def _replace_atomic(src, dst):
2623
os.rename(src, dst)
2724

2825
def _move_atomic(src, dst):
2926
os.link(src, dst)
3027
os.unlink(src)
3128
else:
32-
import win32api
33-
import win32file
34-
import pywintypes
29+
from ctypes import windll, WinError
3530

36-
_windows_default_flags = win32file.MOVEFILE_WRITE_THROUGH
37-
_windows_error_table = {
38-
183: _FileExistsError,
39-
3: _FileNotFoundError
40-
}
31+
_MOVEFILE_REPLACE_EXISTING = 0x1
32+
_MOVEFILE_WRITE_THROUGH = 0x8
33+
_windows_default_flags = _MOVEFILE_WRITE_THROUGHr
4134

42-
@contextlib.contextmanager
43-
def handle_errors():
44-
try:
45-
yield
46-
except pywintypes.error as e:
47-
native_cls = _windows_error_table.get(e.winerror, OSError)
48-
new_e = native_cls(e)
49-
new_e.windows_error = e
50-
raise new_e
35+
def _handle_errors(rv):
36+
if not rv:
37+
raise WinError()
5138

5239
def _replace_atomic(src, dst):
53-
win32api.MoveFileEx(
54-
src, dst,
55-
win32file.MOVEFILE_REPLACE_EXISTING | _windows_default_flags
56-
)
40+
_handle_errors(windll.kernel32.MoveFileExW(
41+
_path_to_unicode(src), _path_to_unicode(dst),
42+
_windows_default_flags | _MOVEFILE_REPLACE_EXISTING
43+
))
5744

5845
def _move_atomic(src, dst):
59-
win32api.MoveFileEx(
60-
src, dst,
46+
_handle_errors(windll.kernel32.MoveFileExW(
47+
_path_to_unicode(src), _path_to_unicode(dst),
6148
_windows_default_flags
62-
)
49+
))
6350

6451

6552
def replace_atomic(src, dst):
@@ -70,8 +57,7 @@ def replace_atomic(src, dst):
7057
Both paths must reside on the same filesystem for the operation to be
7158
atomic.
7259
'''
73-
with handle_errors():
74-
return _replace_atomic(src, dst)
60+
return _replace_atomic(src, dst)
7561

7662

7763
def move_atomic(src, dst):
@@ -83,8 +69,7 @@ def move_atomic(src, dst):
8369
Both paths must reside on the same filesystem for the operation to be
8470
atomic.
8571
'''
86-
with handle_errors():
87-
return _move_atomic(src, dst)
72+
return _move_atomic(src, dst)
8873

8974

9075
class AtomicWriter(object):

docs/index.rst

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ Errorhandling
1313

1414
All filesystem errors are subclasses of :py:exc:`OSError`.
1515

16-
- On UNIX systems, errors from the Python stdlib calls are simply uncaught.
17-
- On Windows systems, PyWin32 errors are wrapped such that they somewhat
18-
resemble the stdlib exceptions. The original PyWin32 exception object is
19-
available as ``e.windows_error`` (with ``e`` being the thrown exception).
16+
- On UNIX systems, errors from the Python stdlib calls are thrown.
17+
- On Windows systems, errors from Python's ``ctypes`` are thrown.
18+
19+
In either case, the ``errno`` attribute on the throws exception maps to an
20+
errorcode in the ``errno`` module.
2021

2122
Low-level API
2223
-------------

setup.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,6 @@
1414
version = str(ast.literal_eval(_version_re.search(
1515
f.read().decode('utf-8')).group(1)))
1616

17-
18-
deps = []
19-
if sys.platform == 'win32':
20-
# http://sourceforge.net/p/pywin32/bugs/680/
21-
deps.append('pypiwin32')
22-
2317
setup(
2418
name='atomicwrites',
2519
version=version,
@@ -31,5 +25,4 @@
3125
long_description=open('README.rst').read(),
3226
packages=find_packages(exclude=['tests.*', 'tests']),
3327
include_package_data=True,
34-
install_requires=deps
3528
)

0 commit comments

Comments
 (0)