Skip to content
This repository was archived by the owner on Oct 24, 2025. It is now read-only.

Commit 143c439

Browse files
committed
Fix AppVeyor build failure
1 parent 43f9041 commit 143c439

File tree

6 files changed

+117
-62
lines changed

6 files changed

+117
-62
lines changed

appveyor.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ environment:
44
VS90COMNTOOLS: "%VS110COMNTOOLS%"
55
VS100COMNTOOLS: "%VS110COMNTOOLS%"
66
matrix:
7+
- PYTHON: "C:\\Python27_32"
8+
PYTHON_VERSION: "2.7.8"
9+
PYTHON_ARCH: "32"
710
- PYTHON: "C:\\Python34_32"
811
PYTHON_VERSION: "3.4.1"
912
PYTHON_ARCH: "32"
@@ -13,6 +16,8 @@ environment:
1316
matrix:
1417
fast_finish: true
1518
init:
19+
- ps: "$env:Path = \"C:\\Program Files (x86)\\Microsoft Visual Studio 11.0\\VC\\bin;\" + $env:Path"
20+
- "ECHO %PATH%"
1621
- "ECHO %PYTHON%"
1722
- ps: "ls C:/Python*"
1823
install:
@@ -21,6 +26,8 @@ install:
2126
- "powershell C:/python-install.ps1"
2227
build: false
2328
test_script:
29+
- "dir \"C:\\Program Files (x86)\""
30+
- "%PYTHON%\\python.exe -c \"import os,pprint;pprint.pprint(os.environ)\""
2431
- "%PYTHON%\\python.exe setup.py test"
2532
after_test:
2633
- ps: (new-object net.webclient).DownloadFile('https://bootstrap.pypa.io/get-pip.py', 'C:/get-pip.py')

sass.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import collections
1414
import os
1515
import os.path
16+
import re
1617
import sys
1718

1819
from six import string_types, text_type
@@ -244,7 +245,37 @@ def compile(**kwargs):
244245
if s:
245246
v = v.decode('utf-8')
246247
if source_map_filename:
247-
v = v, source_map.decode('utf-8')
248+
source_map = source_map.decode('utf-8')
249+
if os.sep != '/' and os.altsep:
250+
# Libsass has a bug that produces invalid JSON string
251+
# literals which contain unescaped backslashes for
252+
# "sources" paths on Windows e.g.:
253+
#
254+
# {
255+
# "version": 3,
256+
# "file": "",
257+
# "sources": ["c:\temp\tmpj2ac07\test\b.scss"],
258+
# "names": [],
259+
# "mappings": "AAAA,EAAE;EAEE,WAAW"
260+
# }
261+
#
262+
# To workaround this bug without changing libsass'
263+
# internal behavior, we replace these backslashes with
264+
# slashes e.g.:
265+
#
266+
# {
267+
# "version": 3,
268+
# "file": "",
269+
# "sources": ["c:/temp/tmpj2ac07/test/b.scss"],
270+
# "names": [],
271+
# "mappings": "AAAA,EAAE;EAEE,WAAW"
272+
# }
273+
source_map = re.sub(
274+
r'"sources":\s*\[\s*"[^"]*"(?:\s*,\s*"[^"]*")*\s*\]',
275+
lambda m: m.group(0).replace(os.sep, os.altsep),
276+
source_map
277+
)
278+
v = v, source_map
248279
return v
249280
elif 'dirname' in modes:
250281
try:

sassc.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
from __future__ import print_function
5555

5656
import functools
57+
import io
5758
import optparse
5859
import os
5960
import sys
@@ -146,16 +147,13 @@ def main(argv=sys.argv, stdout=sys.stdout, stderr=sys.stderr):
146147
if len(args) < 2:
147148
print(css, file=stdout)
148149
else:
149-
with open(args[1], 'w') as f:
150-
try:
151-
print(css, file=f)
152-
except UnicodeEncodeError:
153-
print(css.encode('utf-8'), file=f)
150+
with io.open(args[1], 'w', encoding='utf-8') as f:
151+
f.write(css)
154152
if options.watch:
155153
print(filename, 'is just compiled to', args[1],
156154
file=stdout)
157155
if source_map_filename:
158-
with open(source_map_filename, 'w') as f:
156+
with io.open(source_map_filename, 'w', encoding='utf-8') as f:
159157
f.write(source_map)
160158
if options.watch: # pragma: no cover
161159
# FIXME: we should utilize inotify on Linux, and FSEvents on Mac

sasstests.py

Lines changed: 60 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from __future__ import with_statement
33

44
import collections
5+
import io
56
import json
67
import os
78
import os.path
@@ -10,7 +11,7 @@
1011
import tempfile
1112
import unittest
1213

13-
from six import StringIO, b, text_type
14+
from six import PY3, StringIO, b, text_type
1415
from werkzeug.test import Client
1516
from werkzeug.wrappers import Response
1617

@@ -65,6 +66,8 @@
6566
font: '나눔고딕', sans-serif; }
6667
'''
6768

69+
utf8_if_py3 = {'encoding': 'utf-8'} if PY3 else {}
70+
6871

6972
class SassTestCase(unittest.TestCase):
7073

@@ -221,26 +224,26 @@ def test_regression_issue_11(self):
221224
class BuilderTestCase(unittest.TestCase):
222225

223226
def test_builder_build_directory(self):
224-
temp_path= tempfile.mkdtemp()
227+
temp_path = tempfile.mkdtemp()
225228
sass_path = os.path.join(temp_path, 'sass')
226229
css_path = os.path.join(temp_path, 'css')
227230
shutil.copytree('test', sass_path)
228231
result_files = build_directory(sass_path, css_path)
229232
assert len(result_files) == 4
230233
assert result_files['a.scss'] == 'a.scss.css'
231-
with open(os.path.join(css_path, 'a.scss.css')) as f:
234+
with open(os.path.join(css_path, 'a.scss.css'), **utf8_if_py3) as f:
232235
css = f.read()
233236
assert css == A_EXPECTED_CSS
234237
assert result_files['b.scss'] == 'b.scss.css'
235-
with open(os.path.join(css_path, 'b.scss.css')) as f:
238+
with open(os.path.join(css_path, 'b.scss.css'), **utf8_if_py3) as f:
236239
css = f.read()
237240
assert css == B_EXPECTED_CSS
238241
assert result_files['c.scss'] == 'c.scss.css'
239-
with open(os.path.join(css_path, 'c.scss.css')) as f:
242+
with open(os.path.join(css_path, 'c.scss.css'), **utf8_if_py3) as f:
240243
css = f.read()
241244
assert css == C_EXPECTED_CSS
242245
assert result_files['d.scss'] == 'd.scss.css'
243-
with open(os.path.join(css_path, 'd.scss.css')) as f:
246+
with open(os.path.join(css_path, 'd.scss.css'), **utf8_if_py3) as f:
244247
css = f.read()
245248
self.assertEqual(D_EXPECTED_CSS, css)
246249
shutil.rmtree(temp_path)
@@ -267,51 +270,68 @@ def test_normalize_manifests(self):
267270

268271
def test_build_one(self):
269272
d = tempfile.mkdtemp()
273+
src_path = os.path.join(d, 'test')
274+
if os.sep != '/' and os.altsep:
275+
normalize = lambda p: os.path.abspath(
276+
os.path.normpath(os.path.join(src_path, p))
277+
).replace(os.sep, os.altsep)
278+
else:
279+
normalize = lambda p: p
270280
try:
271-
shutil.copytree('test', os.path.join(d, 'test'))
281+
shutil.copytree('test', src_path)
272282
m = Manifest(sass_path='test', css_path='css')
273283
m.build_one(d, 'a.scss')
274284
with open(os.path.join(d, 'css', 'a.scss.css')) as f:
275285
self.assertEqual(A_EXPECTED_CSS, f.read())
276286
m.build_one(d, 'b.scss', source_map=True)
277-
with open(os.path.join(d, 'css', 'b.scss.css')) as f:
287+
with open(os.path.join(d, 'css', 'b.scss.css'),
288+
**utf8_if_py3) as f:
278289
self.assertEqual(
279290
B_EXPECTED_CSS +
280291
'\n/*# sourceMappingURL=b.scss.css.map */',
281292
f.read()
282293
)
283-
with open(os.path.join(d, 'css', 'b.scss.css.map')) as f:
284-
self.assertEqual(
285-
{
286-
'version': 3,
287-
'file': '',
288-
'sources': ['../test/b.scss'],
289-
'names': [],
290-
'mappings': 'AAAA,EAAE;EAEE,WAAW'
291-
},
292-
json.load(f)
293-
)
294+
self.assert_json_file(
295+
{
296+
'version': 3,
297+
'file': '',
298+
'sources': [normalize('../test/b.scss')],
299+
'names': [],
300+
'mappings': 'AAAA,EAAE;EAEE,WAAW'
301+
},
302+
os.path.join(d, 'css', 'b.scss.css.map')
303+
)
294304
m.build_one(d, 'd.scss', source_map=True)
295-
with open(os.path.join(d, 'css', 'd.scss.css')) as f:
305+
with open(os.path.join(d, 'css', 'd.scss.css'),
306+
**utf8_if_py3) as f:
296307
self.assertEqual(
297308
D_EXPECTED_CSS +
298309
'\n/*# sourceMappingURL=d.scss.css.map */',
299310
f.read()
300311
)
301-
with open(os.path.join(d, 'css', 'd.scss.css.map')) as f:
302-
self.assertEqual(
303-
{
304-
'version': 3,
305-
'file': '',
306-
'sources': ['../test/d.scss'],
307-
'names': [],
308-
'mappings': 'AAKA;EAHE,kBAAkB;EAIpB,KAAK;IAED,MAAM'
309-
},
310-
json.load(f)
311-
)
312+
self.assert_json_file(
313+
{
314+
'version': 3,
315+
'file': '',
316+
'sources': [normalize('../test/d.scss')],
317+
'names': [],
318+
'mappings': 'AAKA;EAHE,kBAAkB;EAIpB,KAAK;IAED,MAAM'
319+
},
320+
os.path.join(d, 'css', 'd.scss.css.map')
321+
)
312322
finally:
313323
shutil.rmtree(d)
314324

325+
def assert_json_file(self, expected, filename):
326+
with open(filename) as f:
327+
try:
328+
tree = json.load(f)
329+
except ValueError as e:
330+
f.seek(0)
331+
msg = '{0!s}\n\n{1}:\n\n{2}'.format(e, filename, f.read())
332+
raise ValueError(msg)
333+
self.assertEqual(expected, tree)
334+
315335

316336
class WsgiTestCase(unittest.TestCase):
317337

@@ -329,19 +349,24 @@ def test_wsgi_sass_middleware(self):
329349
client = Client(app, Response)
330350
r = client.get('/asdf')
331351
self.assertEqual(200, r.status_code)
332-
self.assertEqual(b'/asdf', r.data)
352+
self.assert_bytes_equal(b'/asdf', r.data)
333353
self.assertEqual('text/plain', r.mimetype)
334354
r = client.get('/static/a.scss.css')
335355
self.assertEqual(200, r.status_code)
336-
self.assertEqual(b(A_EXPECTED_CSS_WITH_MAP), r.data)
356+
self.assert_bytes_equal(b(A_EXPECTED_CSS_WITH_MAP), r.data)
337357
self.assertEqual('text/css', r.mimetype)
338358
r = client.get('/static/not-exists.sass.css')
339359
self.assertEqual(200, r.status_code)
340-
self.assertEqual(b'/static/not-exists.sass.css', r.data)
360+
self.assert_bytes_equal(b'/static/not-exists.sass.css', r.data)
341361
self.assertEqual('text/plain', r.mimetype)
342362
finally:
343363
shutil.rmtree(css_dir)
344364

365+
def assert_bytes_equal(self, expected, actual, *args):
366+
self.assertEqual(expected.replace(b'\r\n', b'\n'),
367+
actual.replace(b'\r\n', b'\n'),
368+
*args)
369+
345370

346371
class SasscTestCase(unittest.TestCase):
347372

@@ -397,7 +422,7 @@ def test_sassc_output_unicode(self):
397422
self.assertEqual(0, exit_code)
398423
self.assertEqual('', self.err.getvalue())
399424
self.assertEqual('', self.out.getvalue())
400-
with open(tmp) as f:
425+
with open(tmp, **utf8_if_py3) as f:
401426
self.assertEqual(
402427
D_EXPECTED_CSS.strip(),
403428
f.read().strip()

sassutils/builder.py

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from __future__ import with_statement
66

77
import collections
8+
import io
89
import os
910
import os.path
1011
import re
@@ -49,11 +50,8 @@ def build_directory(sass_path, css_path, _root_sass=None, _root_css=None):
4950
if os.path.isfile(sass_fullname):
5051
css_fullname = os.path.join(css_path, name) + '.css'
5152
css = compile(filename=sass_fullname, include_paths=[_root_sass])
52-
with open(css_fullname, 'w') as css_file:
53-
try:
54-
css_file.write(css)
55-
except UnicodeEncodeError:
56-
css_file.write(css.encode('utf-8'))
53+
with io.open(css_fullname, 'w', encoding='utf-8') as css_file:
54+
css_file.write(css)
5755
result[os.path.relpath(sass_fullname, _root_sass)] = \
5856
os.path.relpath(css_fullname, _root_css)
5957
elif os.path.isdir(sass_fullname):
@@ -195,13 +193,10 @@ def build_one(self, package_dir, filename, source_map=False):
195193
css_folder = os.path.dirname(css_path)
196194
if not os.path.exists(css_folder):
197195
os.makedirs(css_folder)
198-
with open(css_path, 'w') as f:
199-
try:
200-
f.write(css)
201-
except UnicodeEncodeError:
202-
f.write(css.encode('utf-8'))
196+
with io.open(css_path, 'w', encoding='utf-8') as f:
197+
f.write(css)
203198
if source_map:
204-
with open(source_map_path, 'wb') as f:
205-
# Source maps are JSON, and JSON has to be UTF-8 encoded
206-
f.write(source_map.encode('utf-8'))
199+
# Source maps are JSON, and JSON has to be UTF-8 encoded
200+
with io.open(source_map_path, 'w', encoding='utf-8') as f:
201+
f.write(source_map)
207202
return css_filename

setup.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
raise SystemExit(1)
5353

5454
if sys.platform == 'win32':
55-
from distutils.msvccompiler import MSVCCompiler
5655
from distutils.msvc9compiler import get_build_version
5756
vscomntools_env = 'VS{0}{1}COMNTOOLS'.format(
5857
int(get_build_version()),
@@ -63,12 +62,12 @@
6362
except KeyError:
6463
distutils.log.warn('You probably need Visual Studio 2012 (11.0) '
6564
'or higher')
66-
distutils.log.warn('%%%s%%=%s',
67-
vscomntools_env,
68-
os.environ.get(vscomntools_env, ''))
69-
c = MSVCCompiler()
70-
c.initialize()
71-
distutils.log.warn('msvc_paths = %r', c.get_msvc_paths('path'))
65+
from distutils import msvccompiler, msvc9compiler
66+
if msvccompiler.get_build_version() < 11.0:
67+
msvccompiler.get_build_version = lambda: 11.0
68+
if get_build_version() < 11.0:
69+
msvc9compiler.get_build_version = lambda: 11.0
70+
msvc9compiler.VERSION = 11.0
7271
# Workaround http://bugs.python.org/issue4431 under Python <= 2.6
7372
if sys.version_info < (2, 7):
7473
def spawn(self, cmd):

0 commit comments

Comments
 (0)