Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions mincss/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,24 @@
import io
import os
import time
from urllib.parse import urlparse

from mincss import __version__
from .processor import Processor


def filename_from_href(href):
"""Return a filesystem-safe filename derived from a URL's href.

Using the raw last path segment of a URL (including any query string)
as a filename fails on Windows with OSError: [Errno 22] Invalid
argument, since characters like '?' and '=' are not valid in Windows
filenames. This drops the query string and fragment, keeping only the
URL's path basename.
"""
return os.path.basename(urlparse(href).path)


def run(args):
if args.version:
print(__version__)
Expand Down Expand Up @@ -37,10 +50,10 @@ def run(args):
os.mkdir(output_dir)
for link in p.links:
print('FOR', link.href)
orig_name = link.href.split('/')[-1]
orig_name = filename_from_href(link.href)
with io.open(os.path.join(output_dir, orig_name), 'w') as f:
f.write(link.after)
before_name = 'before_' + link.href.split('/')[-1]
before_name = 'before_' + orig_name
with io.open(os.path.join(output_dir, before_name), 'w') as f:
f.write(link.before)
print('Files written to', output_dir)
Expand Down
41 changes: 41 additions & 0 deletions tests/test_main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""
Regression test for GH issue #53 (peterbe/mincss):

OSError: [Errno 22] Invalid argument: './output\\banner-styles.css?v=...'

main.run() derived the output filename for a downloaded CSS link
directly from `link.href.split('/')[-1]`, which includes the URL's
query string verbatim. Characters like '?' and '=' are not valid in
Windows filenames, so writing that file raised OSError (Windows) or
produced a surprising/wrong filename (POSIX, where '?' is legal but
clearly not intended to be part of a filename).

filename_from_href() now parses the URL properly and uses only the
path's basename, dropping the query string and fragment.
"""

import unittest

from mincss.main import filename_from_href


class FilenameFromHrefTest(unittest.TestCase):
def test_strips_query_string(self):
href = "http://example.com/static/css/banner-styles.css?v=1516052760.0"
self.assertEqual(filename_from_href(href), "banner-styles.css")

def test_strips_query_string_and_fragment(self):
href = "http://example.com/style.css?v=1#section"
self.assertEqual(filename_from_href(href), "style.css")

def test_plain_url_without_query_string_unaffected(self):
href = "http://example.com/static/css/plain.css"
self.assertEqual(filename_from_href(href), "plain.css")

def test_relative_path_url(self):
href = "/static/css/relative.css?v=2"
self.assertEqual(filename_from_href(href), "relative.css")


if __name__ == "__main__":
unittest.main()