Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
  • 3 commits
  • 4 files changed
  • 0 comments
  • 1 contributor
Aug 28, 2011
W. Mark Kubacki Output files have the same modification date as their corresponding i…
…nput files.

"ctime" is not used anymore because changing file permissions does not affect the content,
which in turn shouldn't be reflected on any date or time webservers provide browsers with.
9d7518f
W. Mark Kubacki Files can optionally be pre-compressed by GZIP. Will decrease load on…
… busy sites running state-of-the-art webserver software.
309b953
W. Mark Kubacki Version bump to 0.9.1 2f0055b
4  src/thot/__init__.py
@@ -14,6 +14,6 @@
14 14
 # License for the specific language governing permissions and limitations
15 15
 # under the License.
16 16
 
17  
-__version__ = "0.9"
  17
+__version__ = "0.9.1"
18 18
 version = __version__
19  
-version_info = (0, 9)
  19
+version_info = (0, 9, 1)
18  src/thot/app.py
@@ -8,6 +8,7 @@
8 8
                     exists, normpath
9 9
 from shutil import copytree
10 10
 import sys
  11
+import time
11 12
 import pytz
12 13
 import pkg_resources
13 14
 
@@ -16,6 +17,12 @@
16 17
 from thot.template import get_templating_cls
17 18
 
18 19
 LOGGING_LEVELS = {'info': logging.INFO, 'debug': logging.DEBUG}
  20
+GZIP_ENDINGS = [
  21
+    '.css', '.js', '.xml', '.txt', '.sh', '.svg',
  22
+    '.xls', '.doc', '.xjs', '.psd', '.ppt',
  23
+    '.java', '.py', '.pyc', '.pyo', '.bat', '.dll', '.lib',
  24
+    '.cfg', '.ini',
  25
+    ]
19 26
 
20 27
 def quickstart(settings):
21 28
     login = getlogin()
@@ -46,7 +53,7 @@ def quickstart(settings):
46 53
     # before writing the settings file, make sure the _lib dir exists
47 54
     if not exists(settings['lib_dir']):
48 55
         makedirs(settings['lib_dir'])
49  
-    
  56
+
50 57
     with open(settings['settings_path'], 'wb', encoding='utf-8') as configfile:
51 58
         configfile.write(yaml.dump(config, default_flow_style=False))
52 59
 
@@ -63,6 +70,8 @@ def main():
63 70
     parser.add_option('--hardlinks', action="store_true",
64 71
                       help="instead of copying static files, creates hardlinks" \
65 72
                            + " - which is faster and saves space")
  73
+    parser.add_option('-z', '--gzip', action="store_true",
  74
+                      help="make a gzip-compressed copy of rendered files")
66 75
     parser.add_option('-t', '--templating', default='mako',
67 76
                       dest='templating_engine',
68 77
                       help="templating engine (e.g. jinja2, mako) for output")
@@ -74,7 +83,7 @@ def main():
74 83
         project_dir = abspath(args[0])
75 84
     except IndexError:
76 85
         project_dir = abspath(getcwd())
77  
-    
  86
+
78 87
     settings = {'project_dir': project_dir,
79 88
                 'output_dir': join(project_dir, '_output'),
80 89
                 'template_dir': join(project_dir, '_templates'),
@@ -82,8 +91,11 @@ def main():
82 91
                 'url_path': join(project_dir, '_lib', 'urls.py'),
83 92
                 'settings_path': join(project_dir, '_lib', 'settings.cfg'),
84 93
                 'hardlinks': options.hardlinks,
  94
+                'make_compressed_copy': options.gzip,
  95
+                'compress_if_ending': GZIP_ENDINGS,
85 96
                 'templating_engine': options.templating_engine,
86 97
                 'source': options.source,
  98
+                'build_tz': pytz.timezone(time.strftime("%Z", time.gmtime())),
87 99
                 'build_time': pytz.utc.localize(datetime.utcnow())}
88 100
 
89 101
     # configure logging
@@ -91,7 +103,7 @@ def main():
91 103
     logging.basicConfig(level=logging_level,
92 104
                         format='%(asctime)s %(levelname)s: %(message)s',
93 105
                         datefmt='%Y-%m-%d %H:%M:%S')
94  
-    
  106
+
95 107
     # quickstart
96 108
     if options.quickstart:
97 109
         quickstart(settings)
50  src/thot/core.py
... ...
@@ -1,17 +1,18 @@
1  
-from codecs import open
  1
+import codecs
2 2
 from datetime import datetime
3 3
 import imp
4 4
 import logging
5 5
 import types
6  
-from os import makedirs
7  
-from os.path import splitext, join, dirname, split, getctime, \
  6
+from os import makedirs, utime
  7
+from os.path import splitext, join, dirname, split, getmtime, \
8 8
                     basename, exists, relpath, isabs
9  
-from shutil import rmtree, copytree
  9
+from shutil import rmtree, copytree, copystat
10 10
 import sys
11 11
 import time
12 12
 import pytz
13 13
 import pkg_resources
14 14
 import weakref
  15
+import gzip
15 16
 
16 17
 from thot import parser
17 18
 from thot.url import get_url
@@ -216,8 +217,29 @@ def _write(self):
216 217
 
217 218
             # write to filesystem
218 219
             logging.debug("writing %s to %s", page['path'], output_path)
219  
-            with open(output_path, 'w', 'utf-8') as f:
  220
+            with codecs.open(output_path, 'w', 'utf-8') as f:
220 221
                 f.write(rendered)
  222
+            page_dt_for_fs = page['date'].astimezone(self.settings['build_tz'])
  223
+            atime = mtime = int(time.mktime(page_dt_for_fs.timetuple()))
  224
+            utime(output_path, (atime, mtime))
  225
+
  226
+            # GZIP output for webservers which support pre-compressed files
  227
+            if self.settings['make_compressed_copy']:
  228
+                gz_output_path = output_path+'.gz'
  229
+                with gzip.GzipFile(gz_output_path, 'w', mtime=mtime) as f:
  230
+                    f.write(rendered.encode('utf-8'))
  231
+                utime(gz_output_path, (atime, mtime))
  232
+
  233
+    def _copy_static_file(self, static_file, dst):
  234
+        logging.debug('copying %s to %s', static_file, dst)
  235
+        if copy_file(static_file, dst, self.settings['hardlinks']) \
  236
+           and self.settings['make_compressed_copy']:
  237
+            for ending in self.settings['compress_if_ending']:
  238
+                if static_file.endswith(ending):
  239
+                    with open(static_file, 'rb') as fin, gzip.open(dst+'.gz', 'wb') as fout:
  240
+                        fout.writelines(fin)
  241
+                    copystat(static_file, dst+'.gz')
  242
+                    break
221 243
 
222 244
     def _copy_static_files(self):
223 245
         "Copies static files to output directory"
@@ -225,8 +247,7 @@ def _copy_static_files(self):
225 247
         for static_file in self.static_files:
226 248
             dst = join(self.settings['output_dir'],
227 249
                        relpath(static_file, self.settings['project_dir']))
228  
-            logging.debug('copying %s to %s', static_file, dst)
229  
-            copy_file(static_file, dst, self.settings['hardlinks'])
  250
+            self._copy_static_file(static_file, dst)
230 251
 
231 252
         # static files that are associated with pages
232 253
         for page in self.pages:
@@ -234,8 +255,7 @@ def _copy_static_files(self):
234 255
                 dst = join(self.settings['output_dir'],
235 256
                            dirname(self._get_output_path(page['url'])),
236 257
                            relpath(static_file, dirname(page['path'])))
237  
-                logging.debug('copying %s to %s', static_file, dst)
238  
-                copy_file(static_file, dst, self.settings['hardlinks'])
  258
+                self._copy_static_file(static_file, dst)
239 259
 
240 260
     def run(self):
241 261
         start_time = time.time()
@@ -322,7 +342,7 @@ def _create_page(self, path, static_files):
322 342
         return page
323 343
 
324 344
     def read(self, path):
325  
-        with open(join(self.project_dir, path), 'r', encoding='utf-8') as f:
  345
+        with codecs.open(join(self.project_dir, path), 'r', encoding='utf-8') as f:
326 346
             return f.read()
327 347
 
328 348
     def _get_default_headers(self, path):
@@ -331,10 +351,9 @@ def _get_default_headers(self, path):
331 351
 
332 352
         `path` - the relative path from the project dir to the file
333 353
         `title` - titleized version of the filename
334  
-        `date` - set to ctime. On unix this is the time of the most recent
335  
-                 metadata change; on windows the creation time. If ctime
336  
-                 cannot be accessed (due to permissions), the current
337  
-                 time is used.
  354
+        `date` - set to mtime. This is the time of the most recent
  355
+                 content change. If mtime cannot be accessed (due
  356
+                 to permissions), the current time is used.
338 357
         `status` - set to 'live'
339 358
         `template` - set to 'default.html'
340 359
         `url` - set to "default" rule
@@ -350,7 +369,7 @@ def _get_default_headers(self, path):
350 369
             slug = filename
351 370
         title = filename.title()
352 371
         try:
353  
-            date = pytz.utc.localize(datetime.utcfromtimestamp(getctime(path)))
  372
+            date = pytz.utc.localize(datetime.utcfromtimestamp(getmtime(path)))
354 373
         except OSError:
355 374
             # use the current date if the ctime cannot be accessed
356 375
             date = self.build_time
@@ -359,4 +378,3 @@ def _get_default_headers(self, path):
359 378
                     title=title, date=date, status='live',
360 379
                     slug=slug, template=template, url='default',
361 380
                     output_ext=output_ext)
362  
-
7  src/thot/utils.py
@@ -92,16 +92,17 @@ def copy_file(src, dst, hardlinks=False):
92 92
 
93 93
     if os.path.isfile(dst):
94 94
         if equivalent_files(src, dst):
95  
-            return
  95
+            return False
96 96
     try:
97 97
         if hardlinks:
98 98
             try:
99 99
                 os.link(src, dst)
100  
-                return
  100
+                return True
101 101
             except OSError:
102 102
                 logging.debug("Could not create hardlink for '%s'->'%s'.",
103 103
                               src, dst)
104  
-        shutil.copy(src, dst)
  104
+        shutil.copy2(src, dst)
  105
+        return True
105 106
     except IOError:
106 107
         logging.debug("Caught IOError when copying '%s'->'%s'.", src, dst)
107 108
         pass

No commit comments for this range

Something went wrong with that request. Please try again.