Repository: getnikola/nikola

Issue:
if nikola package and nikola site are in different partitions some commands crash (windows)
having nikola in my D: partition, I have no problem building the demo or other test sites in the same partition D:, but doing it in other partition fails.

The problem is code in utils.get_asset_path using os.path.relpath assuming it would always succed; this is not the case in windows, when paths in different partitions will traceback. 

Example nikola crash

``` pytb
c:
C:\tmp>nikola init --demo niko_demo

C:\tmp>py -2.6 -c "import sys; from nikola.main import main; main(sys.argv[1:])"
 init --demo niko_demo
[2013-09-29 20:37] NOTICE: Nikola: A new site with example data has been created
 at niko_demo.
[2013-09-29 20:37] NOTICE: Nikola: See README.txt in that folder for more inform
ation.

C:\tmp>cd niko_demo

C:\tmp\niko_demo>nikola build

C:\tmp\niko_demo>py -2.6 -c "import sys; from nikola.main import main; main(sys.
argv[1:])" build
Scanning posts....done!
Traceback (most recent call last):
  File "C:\Python26\lib\site-packages\doit\doit_cmd.py", line 103, in run
    return self.sub_cmds[command].parse_execute(args)
  File "C:\Python26\lib\site-packages\doit\cmd_base.py", line 60, in parse_execu
te
    return self.execute(params, args)
  File "C:\Python26\lib\site-packages\doit\cmd_base.py", line 226, in execute
    args)
  File "D:\cocos_pristine\nikola_backup\nikola\main.py", line 148, in load_tasks

    tasks = generate_tasks('render_site', self.nikola.gen_tasks('render_site', "
Task"))
  File "C:\Python26\lib\site-packages\doit\loader.py", line 237, in generate_tas
ks
    for task_dict, x_doc in flat_generator(gen_result, gen_doc):
  File "C:\Python26\lib\site-packages\doit\loader.py", line 22, in flat_generato
r
    for item in gen:
  File "D:\cocos_pristine\nikola_backup\nikola\nikola.py", line 804, in gen_task
s
    for task in flatten(pluginInfo.plugin_object.gen_tasks()):
  File "D:\cocos_pristine\nikola_backup\nikola\nikola.py", line 798, in flatten
    for t in task:
  File "D:\cocos_pristine\nikola_backup\nikola\plugins\task\pages.py", line 52,
in gen_tasks
    kw["filters"]):
  File "D:\cocos_pristine\nikola_backup\nikola\nikola.py", line 938, in generic_
page_renderer
    deps.extend(utils.get_asset_path(x, self.THEMES) for x in ('bundles', 'paren
t', 'engine'))
  File "D:\cocos_pristine\nikola_backup\nikola\nikola.py", line 938, in <genexpr
>
    deps.extend(utils.get_asset_path(x, self.THEMES) for x in ('bundles', 'paren
t', 'engine'))
  File "D:\cocos_pristine\nikola_backup\nikola\utils.py", line 594, in get_asset
_path
    return os.path.relpath(candidate, sys_decode(os.getcwd()))
  File "C:\Python26\lib\ntpath.py", line 495, in relpath
    % (path_list[0], start_list[0]))
ValueError: path is on drive D:, start on drive C:

C:\tmp\niko_demo>nikola post

C:\tmp\niko_demo>py -2.6 -c "import sys; from nikola.main import main; main(sys.
argv[1:])" post
Scanning posts....done!
Traceback (most recent call last):
  File "C:\Python26\lib\site-packages\doit\doit_cmd.py", line 103, in run
    return self.sub_cmds[command].parse_execute(args)
  File "C:\Python26\lib\site-packages\doit\cmd_base.py", line 60, in parse_execu
te
    return self.execute(params, args)
  File "C:\Python26\lib\site-packages\doit\cmd_base.py", line 226, in execute
    args)
  File "D:\cocos_pristine\nikola_backup\nikola\main.py", line 148, in load_tasks

    tasks = generate_tasks('render_site', self.nikola.gen_tasks('render_site', "
Task"))
  File "C:\Python26\lib\site-packages\doit\loader.py", line 237, in generate_tas
ks
    for task_dict, x_doc in flat_generator(gen_result, gen_doc):
  File "C:\Python26\lib\site-packages\doit\loader.py", line 22, in flat_generato
r
    for item in gen:
  File "D:\cocos_pristine\nikola_backup\nikola\nikola.py", line 804, in gen_task
s
    for task in flatten(pluginInfo.plugin_object.gen_tasks()):
  File "D:\cocos_pristine\nikola_backup\nikola\nikola.py", line 798, in flatten
    for t in task:
  File "D:\cocos_pristine\nikola_backup\nikola\plugins\task\pages.py", line 52,
in gen_tasks
    kw["filters"]):
  File "D:\cocos_pristine\nikola_backup\nikola\nikola.py", line 938, in generic_
page_renderer
    deps.extend(utils.get_asset_path(x, self.THEMES) for x in ('bundles', 'paren
t', 'engine'))
  File "D:\cocos_pristine\nikola_backup\nikola\nikola.py", line 938, in <genexpr
>
    deps.extend(utils.get_asset_path(x, self.THEMES) for x in ('bundles', 'paren
t', 'engine'))
  File "D:\cocos_pristine\nikola_backup\nikola\utils.py", line 594, in get_asset
_path
    return os.path.relpath(candidate, sys_decode(os.getcwd()))
  File "C:\Python26\lib\ntpath.py", line 495, in relpath
    % (path_list[0], start_list[0]))
ValueError: path is on drive D:, start on drive C:
```



Discussion:
I’m wondering if replacing relative paths with absolute ones will break something (it’s explicitly asking for a relative one, there must be a reason).

I might feel like testing this soon (today or tomorrow); I have a Windows VM handy I need to use soon anyways.

It may be more informative to test in Linux: change from relative to absolute in a local copy then
- run the testsuite
- build the demo with absolute / relative and do a diff between them (I don't know if output is enough stable to make this useful)
- same with other sites you may have

That should give a rough estimate about how much the stronger requirement ( 'relative' ) is needed.


Resolving patch:
diff --git a/nikola/utils.py b/nikola/utils.py
--- a/nikola/utils.py
+++ b/nikola/utils.py
@@ -565,25 +565,26 @@ def get_crumbs(path, is_file=False):
 
 
 def get_asset_path(path, themes, files_folders={'files': ''}):
-    """Checks which theme provides the path with the given asset,
-    and returns the "real" path to the asset, relative to the
-    current directory.
+    """
+    .. versionchanghed:: 6.1.0
+
+    Checks which theme provides the path with the given asset,
+    and returns the "real", absolute path to the asset.
 
     If the asset is not provided by a theme, then it will be checked for
     in the FILES_FOLDERS
 
-    >>> print(get_asset_path('assets/css/rst.css', ['bootstrap', 'base']))
-    nikola/data/themes/base/assets/css/rst.css
+    >>> print(get_asset_path('assets/css/rst.css', ['bootstrap', 'base'])) # doctest: +SKIP
+    [...]/nikola/data/themes/base/assets/css/rst.css
 
-    >>> print(get_asset_path('assets/css/theme.css', ['bootstrap', 'base']))
-    nikola/data/themes/bootstrap/assets/css/theme.css
+    >>> print(get_asset_path('assets/css/theme.css', ['bootstrap', 'base'])) # doctest: +SKIP
+    [...]/nikola/data/themes/bootstrap/assets/css/theme.css
 
-    >>> print(get_asset_path('nikola.py', ['bootstrap', 'base'], {'nikola': ''}))
-    nikola/nikola.py
+    >>> print(get_asset_path('nikola.py', ['bootstrap', 'base'], {'nikola': ''})) # doctest: +SKIP
+    [...]/nikola/nikola.py
 
-    >>> print(get_asset_path('nikola/nikola.py', ['bootstrap', 'base'],
-    ... {'nikola':'nikola'}))
-    nikola/nikola.py
+    >>> print(get_asset_path('nikola/nikola.py', ['bootstrap', 'base'], {'nikola':'nikola'})) # doctest: +SKIP
+    [...]/nikola/nikola.py
 
     """
     for theme_name in themes:
@@ -592,14 +593,14 @@ def get_asset_path(path, themes, files_folders={'files': ''}):
             path
         )
         if os.path.isfile(candidate):
-            return os.path.relpath(candidate, sys_decode(os.getcwd()))
+            return candidate
     for src, rel_dst in files_folders.items():
         candidate = os.path.join(
             src,
-            os.path.relpath(path, rel_dst)
+            os.path.abspath(path)
         )
         if os.path.isfile(candidate):
-            return os.path.relpath(candidate, sys_decode(os.getcwd()))
+            return candidate
 
     # whatever!
     return None


Test patch: