Repository: iterative/dvc

Issue:
All files to code dependency list
```
$ run python script.py data/input data/output
```

script.py should be implicitly included to code dependency.


Discussion:

Resolving patch:
diff --git a/dvc/command/repro.py b/dvc/command/repro.py
--- a/dvc/command/repro.py
+++ b/dvc/command/repro.py
@@ -194,9 +194,10 @@ def reproduce_data_item(self):
             Logger.info('Reproducing run command: {}. Args: {}'.format(
                 self._data_item.data.relative, ' '.join(self.state.argv)))
 
-            data_items_from_args = self.cmd_obj.data_items_from_args(self.state.argv)
+            data_items_from_args, not_data_items_from_args = self.cmd_obj.argv_files_by_type(self.state.argv)
             if cmd.run_and_commit_if_needed(self.state.argv,
                                             data_items_from_args,
+                                            not_data_items_from_args,
                                             self.state.stdout,
                                             self.state.stderr,
                                             self.state.shell,
diff --git a/dvc/command/run.py b/dvc/command/run.py
--- a/dvc/command/run.py
+++ b/dvc/command/run.py
@@ -5,7 +5,7 @@
 from dvc.exceptions import DvcException
 from dvc.git_wrapper import GitWrapper
 from dvc.logger import Logger
-from dvc.path.data_item import NotInDataDirError
+from dvc.path.data_item import NotInDataDirError, NotInGitDirError
 from dvc.repository_change import RepositoryChange
 from dvc.runtime import Runtime
 from dvc.state_file import StateFile
@@ -62,8 +62,10 @@ def run(self):
                 return 1
 
         try:
+            data_items_from_args, not_data_items_from_args = self.argv_files_by_type(self.command_args)
             return self.run_and_commit_if_needed(self.command_args,
-                                                 self.data_items_from_args(self.command_args),
+                                                 data_items_from_args,
+                                                 not_data_items_from_args,
                                                  self.parsed_args.stdout,
                                                  self.parsed_args.stderr,
                                                  self.parsed_args.shell)
@@ -71,20 +73,22 @@ def run(self):
             if self.is_locker:
                 lock.release()
 
-    def run_and_commit_if_needed(self, command_args, command_args_data_items,
+    def run_and_commit_if_needed(self, command_args, data_items_from_args, not_data_items_from_args,
                                  stdout, stderr, shell, check_if_ready=True):
         if check_if_ready and not self.skip_git_actions and not self.git.is_ready_to_go():
             return 1
 
         self.run_command(command_args,
-                         command_args_data_items,
+                         data_items_from_args,
+                         not_data_items_from_args,
                          stdout,
                          stderr,
                          shell)
 
         return self.commit_if_needed('DVC run: {}'.format(' '.join(self.args)))
 
-    def run_command(self, cmd_args, data_items_from_args, stdout=None, stderr=None, shell=False):
+    def run_command(self, cmd_args, data_items_from_args, not_data_items_from_args,
+                    stdout=None, stderr=None, shell=False):
         Logger.debug('Run command with args: {}. Data items from args: {}. stdout={}, stderr={}, shell={}'.format(
                      ' '.join(cmd_args),
                      ', '.join([x.data.dvc for x in data_items_from_args]),
@@ -104,7 +108,7 @@ def run_command(self, cmd_args, data_items_from_args, stdout=None, stderr=None,
         input_set = set(data_items_from_args + self.declaration_input_data_items) - output_set
         input_files_dvc = [x.data.dvc for x in input_set]
 
-        code_dependencies_dvc = self.git.abs_paths_to_dvc(self.code_dependencies)
+        code_dependencies_dvc = self.git.abs_paths_to_dvc(self.code_dependencies + not_data_items_from_args)
 
         result = []
         for data_item in repo_change.changed_data_items:
@@ -155,18 +159,23 @@ def _validate_file_states(repo_change):
 
         return not error
 
-    def data_items_from_args(self, argv):
-        result = []
+    def argv_files_by_type(self, argv):
+        data_items = []
+        not_data_items = []
 
         for arg in argv:
             try:
                 if os.path.isfile(arg):
                     data_item = self.settings.path_factory.data_item(arg)
-                    result.append(data_item)
+                    data_items.append(data_item)
+            except NotInGitDirError as ex:
+                msg = 'File {} from argv is outside of git directory and cannot be traced: {}'
+                Logger.warn(msg.format(arg, ex))
             except NotInDataDirError:
+                not_data_items.append(arg)
                 pass
 
-        return result
+        return data_items, not_data_items
 
     def _data_items_from_params(self, files, param_text):
         if not files:
diff --git a/dvc/path/data_item.py b/dvc/path/data_item.py
--- a/dvc/path/data_item.py
+++ b/dvc/path/data_item.py
@@ -33,6 +33,16 @@ def build(file, data_dir):
         return DataItemInStatusDirError(msg)
 
 
+class NotInGitDirError(NotInDataDirError):
+    def __init__(self, msg):
+        super(NotInGitDirError, self).__init__(msg)
+
+    @staticmethod
+    def build(file, git_dir):
+        msg = 'the file "{}" is not in git directory "{}"'.format(file, git_dir)
+        return NotInGitDirError(msg)
+
+
 class DataItem(object):
     STATE_FILE_SUFFIX = '.state'
     CACHE_FILE_SEP = '_'
@@ -44,6 +54,9 @@ def __init__(self, data_file, git, config, cache_file=None):
 
         self._data = Path(data_file, git)
 
+        if not self._data.abs.startswith(self._git.git_dir_abs):
+            raise NotInGitDirError.build(data_file, self._git.git_dir_abs)
+
         if self._data.abs.startswith(self.state_dir_abs):
             raise DataItemInStatusDirError.build(data_file, self._config.data_dir)
 


Test patch:
diff --git a/tests/test_cmd_run.py b/tests/test_cmd_run.py
--- a/tests/test_cmd_run.py
+++ b/tests/test_cmd_run.py
@@ -56,7 +56,7 @@ def test(self):
         self.settings._args = []
         cmd_run = CmdRun(self.settings)
         with self.assertRaises(RunError):
-            cmd_run.run_command(['echo', 'test'], [], shell=True,
+            cmd_run.run_command(['echo', 'test'], [], [], shell=True,
                                 stdout='file1', stderr='file2')
         pass
 
@@ -75,7 +75,7 @@ def setUp(self):
 
         self.file_name1 = os.path.join('data', 'file1')
         self.file_name2 = os.path.join('data', 'file2')
-        self.state_objs = cmd_run.run_command(['echo', 'test'], [], shell=True,
+        self.state_objs = cmd_run.run_command(['echo', 'test'], [], [], shell=True,
                                               stdout=self.file_name1,
                                               stderr=self.file_name2)