Skip to content

Commit c588d78

Browse files
authored
Merge pull request #148 from mission-liao/feature/windows-support
Feature/windows support
2 parents 30c05c5 + ae15448 commit c588d78

6 files changed

Lines changed: 109 additions & 44 deletions

File tree

appveyor.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
environment:
2+
matrix:
3+
- PYTHON: "C:\\Python27"
4+
- PYTHON: "C:\\Python27-x64"
5+
- PYTHON: "C:\\Python35"
6+
- PYTHON: "C:\\Python35-x64"
7+
- PYTHON: "C:\\Python36"
8+
- PYTHON: "C:\\Python36-x64"
9+
10+
install:
11+
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
12+
- "python --version"
13+
- "python -c \"import struct; print(struct.calcsize('P') * 8)\""
14+
- "pip install --disable-pip-version-check --user --upgrade pip"
15+
- "pip install -r requirements-dev.txt"
16+
17+
build: off
18+
19+
test_script:
20+
- "python -m pytest -s -v pyswagger/tests"

pyswagger/tests/test_utils.py

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
from pyswagger import utils, errs
2+
from .utils import is_windows, is_py2
23
from datetime import datetime
34
import unittest
45
import functools
56
import six
7+
import os
68

79

810
class SwaggerUtilsTestCase(unittest.TestCase):
@@ -118,10 +120,16 @@ def test_import_string(self):
118120
self.assertEqual(utils.import_string('qoo_%^&%&'), None)
119121
self.assertNotEqual(utils.import_string('pyswagger'), None)
120122

121-
def test_path2url(self):
123+
@unittest.skipUnless(not is_windows(), 'make no sense on windows')
124+
def test_path2url_on_unix(self):
122125
""" test path2url """
123126
self.assertEqual(utils.path2url('/opt/local/a.json'), 'file:///opt/local/a.json')
124127

128+
@unittest.skipUnless(is_windows(), 'make no sense on unix')
129+
def test_path2url_on_windows(self):
130+
""" test path2url on windows """
131+
self.assertEqual(utils.path2url(r'C:\opt\local\a.json'), 'file:///C:/opt/local/a.json')
132+
125133
def test_jr_split(self):
126134
""" test jr_split """
127135
self.assertEqual(utils.jr_split(
@@ -133,12 +141,6 @@ def test_jr_split(self):
133141
self.assertEqual(utils.jr_split(
134142
'#/definitions/s1'), (
135143
'', '#/definitions/s1'))
136-
self.assertEqual(utils.jr_split(
137-
'/user/tmp/local/ttt'), (
138-
'file:///user/tmp/local/ttt', '#'))
139-
self.assertEqual(utils.jr_split(
140-
'/user/tmp/local/ttt/'), (
141-
'file:///user/tmp/local/ttt', '#'))
142144
# relative path should be converted to absolute one
143145
self.assertEqual(utils.jr_split(
144146
'user'), (
@@ -150,11 +152,34 @@ def test_jr_split(self):
150152
'//'), (
151153
'', '#'))
152154

155+
@unittest.skipUnless(not is_windows(), 'make no sense on windows')
156+
def test_jr_split_on_unix(self):
157+
""" test jr_split on unix-like os """
158+
self.assertEqual(utils.jr_split(
159+
'/user/tmp/local/ttt'), (
160+
'file:///user/tmp/local/ttt', '#'))
161+
self.assertEqual(utils.jr_split(
162+
'/user/tmp/local/ttt/'), (
163+
'file:///user/tmp/local/ttt', '#'))
164+
165+
@unittest.skipUnless(is_windows(), 'make no sense on unix')
166+
def test_jr_split_on_windows(self):
167+
""" test jr_split on windows """
168+
target = 'file:///C:/user/tmp/local/ttt' if is_py2() else 'file:///c:/user/tmp/local/ttt'
169+
170+
self.assertEqual(utils.jr_split(r'C:\user\tmp\local\ttt'), (target, '#'))
171+
self.assertEqual(utils.jr_split(
172+
# check here for adding backslach at the end of raw string
173+
# https://pythonconquerstheuniverse.wordpress.com/2008/06/04/gotcha-%E2%80%94-backslashes-in-windows-filenames/
174+
os.path.normpath('C:/user/tmp/local/ttt/')
175+
), (target, '#'))
176+
153177
def test_cycle_guard(self):
154178
c = utils.CycleGuard()
155179
c.update(1)
156180
self.assertRaises(errs.CycleDetectionError, c.update, 1)
157181

182+
@unittest.skipUnless(not is_windows(), 'make no sense on windows')
158183
def test_normalize_url(self):
159184
self.assertEqual(utils.normalize_url(None), None)
160185
self.assertEqual(utils.normalize_url(''), '')
@@ -163,6 +188,10 @@ def test_normalize_url(self):
163188
self.assertEqual(utils.normalize_url('/tmp/local/test'), 'file:///tmp/local/test')
164189
self.assertEqual(utils.normalize_url('/tmp/local/test in space.txt'), 'file:///tmp/local/test%20in%20space.txt')
165190

191+
@unittest.skipUnless(is_windows(), 'make no sense on unix')
192+
def test_normalize_url_on_windows(self):
193+
self.assertEqual(utils.normalize_url(r'C:\path\to\something'), 'file:///C:/path/to/something')
194+
166195
def test_normalize_jr(self):
167196
self.assertEqual(utils.normalize_jr(None), None)
168197
self.assertEqual(utils.normalize_jr(None, 'http://test.com/api/swagger.json'), None)
@@ -248,6 +277,7 @@ def test_url_join(self):
248277
self.assertEqual(utils.url_join('https://localhost/test', 'swagger.json'), 'https://localhost/test/swagger.json')
249278
self.assertEqual(utils.url_join('https://localhost/test/', 'swagger.json'), 'https://localhost/test/swagger.json')
250279

280+
@unittest.skipUnless(not is_windows(), 'make no sense on windows')
251281
def test_patch_path(self):
252282
""" make sure patch_path works
253283
"""
@@ -256,6 +286,13 @@ def test_patch_path(self):
256286
'/Users/sudeep.agarwal/src/squiddy/api/v0.1/swagger.yaml',
257287
), '/Users/sudeep.agarwal/src/squiddy/api/v0.1/swagger.yaml')
258288

289+
@unittest.skipUnless(is_windows(), 'make no sense on unix-like os')
290+
def test_patch_path_on_windows(self):
291+
self.assertEqual(utils.patch_path(
292+
'Users/sudeep.agarwal/src/squiddy/api/v0.1',
293+
'Users/sudeep.agarwal/src/squiddy/api/v0.1/swagger.yaml',
294+
), 'Users/sudeep.agarwal/src/squiddy/api/v0.1/swagger.yaml')
295+
259296

260297
class WalkTestCase(unittest.TestCase):
261298
""" test for walk """

pyswagger/tests/utils.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import absolute_import
22
import os
3+
import sys
34

45
def get_test_data_folder(version='1.2', which=''):
56
"""
@@ -55,3 +56,10 @@ def create_pet_db():
5556
pet.create_(**pet_Sue)
5657

5758
return pet
59+
60+
def is_windows():
61+
return os.name == 'nt'
62+
63+
def is_py2():
64+
return sys.version_info.major < 3
65+

pyswagger/tests/v1_2/test_upgrade.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,19 @@
11
from pyswagger import App, errs
2-
from ..utils import get_test_data_folder
2+
from pyswagger.utils import normalize_url
3+
from ..utils import get_test_data_folder, is_windows
34
from ...spec.v2_0 import objects
45
import unittest
56
import os
67
import six
78

89

9-
folder = get_test_data_folder(
10+
folder = normalize_url(get_test_data_folder(
1011
version='1.2',
1112
which='wordnik'
12-
)
13+
))
1314

1415
def _pf(s):
15-
return six.moves.urllib.parse.urlunparse((
16-
'file',
17-
'',
18-
folder,
19-
'',
20-
'',
21-
s))
16+
return folder + '#' + s
2217

2318

2419
class Swagger_Upgrade_TestCase(unittest.TestCase):
@@ -128,7 +123,7 @@ def test_parameter(self):
128123
p = [p for p in o.parameters if getattr(p, 'in') == 'formData' and p.type == 'string'][0]
129124
self.assertEqual(p.name, 'additionalMetadata')
130125
self.assertEqual(p.required, False)
131-
126+
132127
# file
133128
o = self.app.root.paths['/api/pet/uploadImage'].post
134129
p = [p for p in o.parameters if getattr(p, 'in') == 'formData' and p.type == 'file'][0]

pyswagger/tests/v2_0/test_circular.py

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from pyswagger import App, utils, primitives, errs
22
from ..utils import get_test_data_folder
3-
from ...scanner import CycleDetector
3+
from ...scanner import CycleDetector
44
from ...scan import Scanner
55
import unittest
66
import os
@@ -20,19 +20,13 @@ def test_path_item_prepare_with_cycle(self):
2020
app.prepare()
2121

2222
def test_path_item(self):
23-
folder = get_test_data_folder(
23+
folder = utils.normalize_url(get_test_data_folder(
2424
version='2.0',
2525
which=os.path.join('circular', 'path_item')
26-
)
26+
))
2727

2828
def _pf(s):
29-
return six.moves.urllib.parse.urlunparse((
30-
'file',
31-
'',
32-
folder,
33-
'',
34-
'',
35-
s))
29+
return folder + '#' + s
3630

3731
app = App.create(folder)
3832
s = Scanner(app)
@@ -47,19 +41,13 @@ def _pf(s):
4741
]]))
4842

4943
def test_schema(self):
50-
folder = get_test_data_folder(
44+
folder = utils.normalize_url(get_test_data_folder(
5145
version='2.0',
5246
which=os.path.join('circular', 'schema')
53-
)
47+
))
5448

5549
def _pf(s):
56-
return six.moves.urllib.parse.urlunparse((
57-
'file',
58-
'',
59-
folder,
60-
'',
61-
'',
62-
s))
50+
return folder + '#' + s
6351

6452

6553
app = App.load(folder)
@@ -99,4 +87,4 @@ def test_primfactory(self):
9987

10088
s = app.resolve('#/definitions/s1')
10189
self.assertRaises(errs.CycleDetectionError, app.prim_factory.produce, s, {})
102-
90+

pyswagger/utils.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -298,20 +298,26 @@ def path2url(p):
298298
""" Return file:// URL from a filename.
299299
"""
300300
# Python 3 is a bit different and does a better job.
301-
if sys.version_info[0] >= 3:
301+
if sys.version_info.major >= 3 and sys.version_info.minor >= 4:
302302
import pathlib
303303
return pathlib.Path(p).as_uri()
304304
else:
305305
return six.moves.urllib.parse.urljoin(
306306
'file:', six.moves.urllib.request.pathname2url(p)
307307
)
308308

309+
_windows_path_prefix = re.compile(r'(^[A-Za-z]:\\)')
310+
309311
def normalize_url(url):
310312
""" Normalize url
311313
"""
312314
if not url:
313315
return url
314316

317+
matched = _windows_path_prefix.match(url)
318+
if matched:
319+
return path2url(url)
320+
315321
p = six.moves.urllib.parse.urlparse(url)
316322
if p.scheme == '':
317323
if p.netloc == '' and p.path != '':
@@ -339,10 +345,18 @@ def url_join(url, path):
339345
""" url version of os.path.join
340346
"""
341347
p = six.moves.urllib.parse.urlparse(url)
342-
t = ('/'.join([p.path, path]),)
348+
349+
t = None
350+
if p.path and p.path[-1] == '/':
351+
if path and path[0] == '/':
352+
path = path[1:]
353+
t = ''.join([p.path, path])
354+
else:
355+
t = ('' if path and path[0] == '/' else '/').join([p.path, path])
356+
343357
return six.moves.urllib.parse.urlunparse(
344358
p[:2]+
345-
t+ # os.sep is different on windows, don't use it here.
359+
(t,)+ # os.sep is different on windows, don't use it here.
346360
p[3:]
347361
)
348362

@@ -382,6 +396,11 @@ def normalize_jr(jr, url=None):
382396
else:
383397
return '#' + jp
384398

399+
def _fullmatch(regex, chunk):
400+
m = re.match(regex, chunk)
401+
if m and m.span()[1] == len(chunk):
402+
return m
403+
385404
def derelativise_url(url):
386405
'''
387406
Normalizes URLs, gets rid of .. and .
@@ -396,14 +415,12 @@ def derelativise_url(url):
396415
newpath=newpath[:-1]
397416
continue
398417
# TODO: Verify this behaviour.
399-
elif re.fullmatch(r'\.{3,}', chunk) is not None:
418+
elif _fullmatch(r'\.{3,}', chunk) is not None:
400419
# parent dir.
401420
newpath=newpath[:-1]
402421
continue
403422
newpath += [chunk]
404423
return six.moves.urllib.parse.urlunparse(parsed[:2]+('/'+('/'.join(newpath)),)+parsed[3:])
405-
def is_file_url(url):
406-
return url.startswith('file://')
407424

408425
def get_swagger_version(obj):
409426
""" get swagger version from loaded json """

0 commit comments

Comments
 (0)