Repository: iterative/dvc

Issue:
Default Dvcfile name for single output command
If `<filename>` is a single output then `<filename>.dvc` might be a good default option for state file.

```
$ dvc run -o data/Posts.xml -d data/Posts.xml.tgz tar zxf data/Posts.xml.tgz -C data/
$ ls data/Posts.xml.dvc
```

`dvc add` already uses this logic.


Discussion:
Btw... the first specified output could be used for multi-output commands. A warning will be probably needed.

Resolving patch:
diff --git a/dvc/cli.py b/dvc/cli.py
--- a/dvc/cli.py
+++ b/dvc/cli.py
@@ -117,7 +117,6 @@ def parse_args(argv=None):
                         help='Lock data item - disable reproduction.')
     run_parser.add_argument('-f',
                         '--file',
-                        default=StateFile.DVCFILE_NAME,
                         help='Specify name of the state file')
     run_parser.add_argument('-c',
                         '--cwd',
diff --git a/dvc/command/run.py b/dvc/command/run.py
--- a/dvc/command/run.py
+++ b/dvc/command/run.py
@@ -1,7 +1,6 @@
 import os
 
 from dvc.command.common.base import CmdBase
-from dvc.data_cloud import file_md5
 from dvc.exceptions import DvcException
 from dvc.logger import Logger
 from dvc.state_file import StateFile
@@ -20,20 +19,35 @@ def __init__(self, settings):
     def run(self):
         cmd = ' '.join(self.parsed_args.command)
 
-        if os.path.isfile(self.parsed_args.file):
-            Logger.error("Stage file {} already exists".format(self.parsed_args.file))
+        stage_file = self.get_stage_file()
+        if os.path.isfile(stage_file):
+            Logger.error("Stage file {} already exists".format(stage_file))
             return 1
 
-        state = StateFile(fname=os.path.join(self.parsed_args.cwd, self.parsed_args.file),
+        state = StateFile(fname=os.path.join(self.parsed_args.cwd, stage_file),
                           cmd=cmd,
                           out=self.parsed_args.out,
                           out_git=self.parsed_args.out_git,
                           deps=self.parsed_args.deps,
-                          locked=self.parsed_args.lock)
+                          locked=self.parsed_args.lock,
+                          cwd=self.parsed_args.cwd)
 
         self.run_command(self.settings, state)
         return self.commit_if_needed('DVC run: {}'.format(state.cmd))
 
+    def get_stage_file(self):
+        if self.parsed_args.file:
+            return self.parsed_args.file
+
+        if self.parsed_args.out or self.parsed_args.out_git:
+            result = self.parsed_args.out[0] if self.parsed_args.out else self.parsed_args.out_git[0]
+            result += StateFile.STATE_FILE_SUFFIX
+            Logger.info(u'Using \'{}\' as a stage file'.format(result))
+            return result
+
+        Logger.info(u'Using \'{}\' as stage file'.format(StateFile.DVCFILE_NAME))
+        return StateFile.DVCFILE_NAME
+
     @staticmethod
     def run_command(settings, state):
         Executor.exec_cmd_only_success(state.cmd, cwd=state.cwd, shell=True)
diff --git a/dvc/state_file.py b/dvc/state_file.py
--- a/dvc/state_file.py
+++ b/dvc/state_file.py
@@ -40,7 +40,8 @@ def __init__(self,
                  out=None,
                  out_git=None,
                  deps=None,
-                 locked=None):
+                 locked=None,
+                 cwd=None):
         super(StateFile, self).__init__()
 
         self.cmd = cmd
@@ -50,7 +51,7 @@ def __init__(self,
         self.locked = locked
 
         self.path = os.path.abspath(fname) if fname else None
-        self.cwd = os.path.dirname(self.path) if self.path else None
+        self.cwd = cwd
 
     @staticmethod
     def parse_deps_state(settings, deps, currdir=None):


Test patch: