We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
conan install fails to distinguish a path from a reference in some circumstances 
Hello!

I just noticed that conan 1.9.1 (at least) is not checking the nature of the `path_or_reference` argument to the `install` command properly. Specifically, if you pass it a `conanfile.txt` (or `conanfile.py`) with a relative path made of 3 or 4 elements it will consider it as reference instead of a path.

Here is a straight forward way to reproduce the problem:
```#!bash
$ mkdir -p foo/bar/baz
$ echo '[requires]' > foo/bar/baz/conanfile.txt
$ conan install foo/bar/baz/conanfile.txt
Configuration:
[settings]
os=Linux
os_build=Linux
arch=x86_64
arch_build=x86_64
compiler=gcc
compiler.version=5
compiler.libcxx=libstdc++
build_type=Release
[options]
[build_requires]
[env]

foo/bar@baz/conanfile.txt: Not found in local cache, looking in remotes...
foo/bar@baz/conanfile.txt: Trying with 'conan-pix4d'...
ERROR: Failed requirement 'foo/bar@baz/conanfile.txt' from 'PROJECT'
ERROR: Unable to find 'foo/bar@baz/conanfile.txt' in remotes
```

Using a shorter (or two element longer) path works:
```#!bash
$ cd foo
$ conan install bar/baz/conanfile.txt 
Configuration:
[settings]
os=Linux
os_build=Linux
arch=x86_64
arch_build=x86_64
compiler=gcc
compiler.version=5
compiler.libcxx=libstdc++
build_type=Release
[options]
[build_requires]
[env]

PROJECT: Installing /tmp/foo/bar/baz/conanfile.txt
Requirements
Packages

PROJECT: Generator txt created conanbuildinfo.txt
PROJECT: Generated conaninfo.txt
```

You can work around the problem easily by prefixing the path with `./` as it will then produce an invalid name for the reference and then conan will fall back to the path scenario.

A possible fix would be:
```
--- conans/client/command.py.bak       2018-11-16 17:18:46.984235498 +0100
+++ conans/client/command.py   2018-11-16 17:23:21.910007591 +0100
@@ -370,6 +370,8 @@
         info = None
         try:
             try:
+                if os.path.isfile(args.path_or_reference):
+                    raise ConanException()
                 reference = ConanFileReference.loads(args.path_or_reference)
             except ConanException:
                 info = self._conan.install(path=args.path_or_reference,
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `conans/model/ref.py`
Content:
```
1 from collections import namedtuple
2 
3 import re
4 from six import string_types
5 
6 from conans.errors import ConanException, InvalidNameException
7 from conans.model.version import Version
8 
9 
10 def check_valid_ref(ref, allow_pattern):
11     try:
12         if not isinstance(ref, ConanFileReference):
13             ref = ConanFileReference.loads(ref, validate=True)
14         return "*" not in ref or allow_pattern
15     except ConanException:
16         pass
17     return False
18 
19 
20 class ConanName(object):
21     _max_chars = 51
22     _min_chars = 2
23     _validation_pattern = re.compile("^[a-zA-Z0-9_][a-zA-Z0-9_\+\.-]{%s,%s}$"
24                                      % (_min_chars - 1, _max_chars - 1))
25 
26     _validation_revision_pattern = re.compile("^[a-zA-Z0-9]{1,%s}$" % _max_chars)
27 
28     @staticmethod
29     def invalid_name_message(value, reference_token=None):
30         if len(value) > ConanName._max_chars:
31             reason = "is too long. Valid names must contain at most %s characters."\
32                      % ConanName._max_chars
33         elif len(value) < ConanName._min_chars:
34             reason = "is too short. Valid names must contain at least %s characters."\
35                      % ConanName._min_chars
36         else:
37             reason = ("is an invalid name. Valid names MUST begin with a "
38                       "letter, number or underscore, have between %s-%s chars, including "
39                       "letters, numbers, underscore, dot and dash"
40                       % (ConanName._min_chars, ConanName._max_chars))
41         message = "Value provided{ref_token}, '{value}' (type {type}), {reason}".format(
42             ref_token=" for {}".format(reference_token) if reference_token else "",
43             value=value, type=type(value).__name__, reason=reason
44         )
45         raise InvalidNameException(message)
46 
47     @staticmethod
48     def validate_string(value, reference_token=None):
49         """Check for string"""
50         if not isinstance(value, string_types):
51             message = "Value provided{ref_token}, '{value}' (type {type}), {reason}".format(
52                 ref_token=" for {}".format(reference_token) if reference_token else "",
53                 value=value, type=type(value).__name__,
54                 reason="is not a string"
55             )
56             raise InvalidNameException(message)
57 
58     @staticmethod
59     def validate_name(name, version=False, reference_token=None):
60         """Check for name compliance with pattern rules"""
61         ConanName.validate_string(name, reference_token=reference_token)
62         if name == "*":
63             return
64         if ConanName._validation_pattern.match(name) is None:
65             if version and name.startswith("[") and name.endswith("]"):
66                 return
67             ConanName.invalid_name_message(name, reference_token=reference_token)
68 
69     @staticmethod
70     def validate_revision(revision):
71         if ConanName._validation_revision_pattern.match(revision) is None:
72             raise InvalidNameException("The revision field, must contain only letters "
73                                        "and numbers with a length between 1 and "
74                                        "%s" % ConanName._max_chars)
75 
76 
77 class ConanFileReference(namedtuple("ConanFileReference", "name version user channel revision")):
78     """ Full reference of a package recipes, e.g.:
79     opencv/2.4.10@lasote/testing
80     """
81     whitespace_pattern = re.compile(r"\s+")
82     sep_pattern = re.compile("@|/|#")
83 
84     def __new__(cls, name, version, user, channel, revision=None, validate=True):
85         """Simple name creation.
86         @param name:        string containing the desired name
87         @param version:     string containing the desired version
88         @param user:        string containing the user name
89         @param channel:     string containing the user channel
90         @param revision:    string containing the revision (optional)
91         """
92         version = Version(version)
93         obj = super(cls, ConanFileReference).__new__(cls, name, version, user, channel, revision)
94         if validate:
95             obj.validate()
96         return obj
97 
98     def validate(self):
99         ConanName.validate_name(self.name, reference_token="package name")
100         ConanName.validate_name(self.version, True, reference_token="package version")
101         ConanName.validate_name(self.user, reference_token="user name")
102         ConanName.validate_name(self.channel, reference_token="channel")
103         if self.revision:
104             ConanName.validate_revision(self.revision)
105 
106     def __hash__(self):
107         return hash((self.name, self.version, self.user, self.channel, self.revision))
108 
109     @staticmethod
110     def loads(text, validate=True):
111         """ Parses a text string to generate a ConanFileReference object
112         """
113         text = ConanFileReference.whitespace_pattern.sub("", text)
114         tokens = ConanFileReference.sep_pattern.split(text)
115         try:
116             if len(tokens) not in (4, 5):
117                 raise ValueError
118             name, version, user, channel = tokens[0:4]
119             revision = tokens[4] if len(tokens) == 5 else None
120         except ValueError:
121             raise ConanException("Wrong package recipe reference %s\nWrite something like "
122                                  "OpenCV/1.0.6@user/stable" % text)
123         obj = ConanFileReference(name, version, user, channel, revision, validate=validate)
124         return obj
125 
126     def __repr__(self):
127         return "%s/%s@%s/%s" % (self.name, self.version, self.user, self.channel)
128 
129     def full_repr(self):
130         str_rev = "#%s" % self.revision if self.revision else ""
131         return "%s%s" % (str(self), str_rev)
132 
133     def dir_repr(self):
134         return "/".join(self[:-1])
135 
136     def copy_with_rev(self, revision):
137         return ConanFileReference(self.name, self.version, self.user, self.channel, revision)
138 
139     def copy_clear_rev(self):
140         return ConanFileReference(self.name, self.version, self.user, self.channel, None)
141 
142 
143 class PackageReference(namedtuple("PackageReference", "conan package_id revision")):
144     """ Full package reference, e.g.:
145     opencv/2.4.10@lasote/testing, fe566a677f77734ae
146     """
147 
148     def __new__(cls, conan, package_id, revision=None, validate=True):
149         if "#" in package_id:
150             package_id, revision = package_id.rsplit("#", 1)
151         obj = super(cls, PackageReference).__new__(cls, conan, package_id, revision)
152         if validate:
153             obj.validate()
154         return obj
155 
156     def validate(self):
157         if self.revision:
158             ConanName.validate_revision(self.revision)
159 
160     @staticmethod
161     def loads(text, validate=True):
162         text = text.strip()
163         tmp = text.split(":")
164         try:
165             conan = ConanFileReference.loads(tmp[0].strip())
166             package_id = tmp[1].strip()
167         except IndexError:
168             raise ConanException("Wrong package reference  %s" % text)
169         return PackageReference(conan, package_id, validate=validate)
170 
171     def __repr__(self):
172         return "%s:%s" % (self.conan, self.package_id)
173 
174     def full_repr(self):
175         str_rev = "#%s" % self.revision if self.revision else ""
176         tmp = "%s:%s%s" % (self.conan.full_repr(), self.package_id, str_rev)
177         return tmp
178 
179     def copy_with_revs(self, revision, p_revision):
180         return PackageReference(self.conan.copy_with_rev(revision), self.package_id, p_revision)
181 
182     def copy_clear_rev(self):
183         ref = self.conan.copy_clear_rev()
184         return PackageReference(ref, self.package_id, revision=None)
185
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/conans/model/ref.py b/conans/model/ref.py
--- a/conans/model/ref.py
+++ b/conans/model/ref.py
@@ -79,7 +79,7 @@
     opencv/2.4.10@lasote/testing
     """
     whitespace_pattern = re.compile(r"\s+")
-    sep_pattern = re.compile("@|/|#")
+    sep_pattern = re.compile(r"([^/]+)/([^/]+)@([^/]+)/([^/#]+)#?(.+)?")
 
     def __new__(cls, name, version, user, channel, revision=None, validate=True):
         """Simple name creation.
@@ -111,12 +111,9 @@
         """ Parses a text string to generate a ConanFileReference object
         """
         text = ConanFileReference.whitespace_pattern.sub("", text)
-        tokens = ConanFileReference.sep_pattern.split(text)
         try:
-            if len(tokens) not in (4, 5):
-                raise ValueError
-            name, version, user, channel = tokens[0:4]
-            revision = tokens[4] if len(tokens) == 5 else None
+            # Split returns empty start and end groups
+            _, name, version, user, channel, revision, _ = ConanFileReference.sep_pattern.split(text)
         except ValueError:
             raise ConanException("Wrong package recipe reference %s\nWrite something like "
                                  "OpenCV/1.0.6@user/stable" % text)

{"golden_diff": "diff --git a/conans/model/ref.py b/conans/model/ref.py\n--- a/conans/model/ref.py\n+++ b/conans/model/ref.py\n@@ -79,7 +79,7 @@\n     opencv/2.4.10@lasote/testing\n     \"\"\"\n     whitespace_pattern = re.compile(r\"\\s+\")\n-    sep_pattern = re.compile(\"@|/|#\")\n+    sep_pattern = re.compile(r\"([^/]+)/([^/]+)@([^/]+)/([^/#]+)#?(.+)?\")\n \n     def __new__(cls, name, version, user, channel, revision=None, validate=True):\n         \"\"\"Simple name creation.\n@@ -111,12 +111,9 @@\n         \"\"\" Parses a text string to generate a ConanFileReference object\n         \"\"\"\n         text = ConanFileReference.whitespace_pattern.sub(\"\", text)\n-        tokens = ConanFileReference.sep_pattern.split(text)\n         try:\n-            if len(tokens) not in (4, 5):\n-                raise ValueError\n-            name, version, user, channel = tokens[0:4]\n-            revision = tokens[4] if len(tokens) == 5 else None\n+            # Split returns empty start and end groups\n+            _, name, version, user, channel, revision, _ = ConanFileReference.sep_pattern.split(text)\n         except ValueError:\n             raise ConanException(\"Wrong package recipe reference %s\\nWrite something like \"\n                                  \"OpenCV/1.0.6@user/stable\" % text)\n", "issue": "conan install fails to distinguish a path from a reference in some circumstances \nHello!\r\n\r\nI just noticed that conan 1.9.1 (at least) is not checking the nature of the `path_or_reference` argument to the `install` command properly. Specifically, if you pass it a `conanfile.txt` (or `conanfile.py`) with a relative path made of 3 or 4 elements it will consider it as reference instead of a path.\r\n\r\nHere is a straight forward way to reproduce the problem:\r\n```#!bash\r\n$ mkdir -p foo/bar/baz\r\n$ echo '[requires]' > foo/bar/baz/conanfile.txt\r\n$ conan install foo/bar/baz/conanfile.txt\r\nConfiguration:\r\n[settings]\r\nos=Linux\r\nos_build=Linux\r\narch=x86_64\r\narch_build=x86_64\r\ncompiler=gcc\r\ncompiler.version=5\r\ncompiler.libcxx=libstdc++\r\nbuild_type=Release\r\n[options]\r\n[build_requires]\r\n[env]\r\n\r\nfoo/bar@baz/conanfile.txt: Not found in local cache, looking in remotes...\r\nfoo/bar@baz/conanfile.txt: Trying with 'conan-pix4d'...\r\nERROR: Failed requirement 'foo/bar@baz/conanfile.txt' from 'PROJECT'\r\nERROR: Unable to find 'foo/bar@baz/conanfile.txt' in remotes\r\n```\r\n\r\nUsing a shorter (or two element longer) path works:\r\n```#!bash\r\n$ cd foo\r\n$ conan install bar/baz/conanfile.txt \r\nConfiguration:\r\n[settings]\r\nos=Linux\r\nos_build=Linux\r\narch=x86_64\r\narch_build=x86_64\r\ncompiler=gcc\r\ncompiler.version=5\r\ncompiler.libcxx=libstdc++\r\nbuild_type=Release\r\n[options]\r\n[build_requires]\r\n[env]\r\n\r\nPROJECT: Installing /tmp/foo/bar/baz/conanfile.txt\r\nRequirements\r\nPackages\r\n\r\nPROJECT: Generator txt created conanbuildinfo.txt\r\nPROJECT: Generated conaninfo.txt\r\n```\r\n\r\nYou can work around the problem easily by prefixing the path with `./` as it will then produce an invalid name for the reference and then conan will fall back to the path scenario.\r\n\r\nA possible fix would be:\r\n```\r\n--- conans/client/command.py.bak       2018-11-16 17:18:46.984235498 +0100\r\n+++ conans/client/command.py   2018-11-16 17:23:21.910007591 +0100\r\n@@ -370,6 +370,8 @@\r\n         info = None\r\n         try:\r\n             try:\r\n+                if os.path.isfile(args.path_or_reference):\r\n+                    raise ConanException()\r\n                 reference = ConanFileReference.loads(args.path_or_reference)\r\n             except ConanException:\r\n                 info = self._conan.install(path=args.path_or_reference,\r\n```\n", "before_files": [{"content": "from collections import namedtuple\n\nimport re\nfrom six import string_types\n\nfrom conans.errors import ConanException, InvalidNameException\nfrom conans.model.version import Version\n\n\ndef check_valid_ref(ref, allow_pattern):\n    try:\n        if not isinstance(ref, ConanFileReference):\n            ref = ConanFileReference.loads(ref, validate=True)\n        return \"*\" not in ref or allow_pattern\n    except ConanException:\n        pass\n    return False\n\n\nclass ConanName(object):\n    _max_chars = 51\n    _min_chars = 2\n    _validation_pattern = re.compile(\"^[a-zA-Z0-9_][a-zA-Z0-9_\\+\\.-]{%s,%s}$\"\n                                     % (_min_chars - 1, _max_chars - 1))\n\n    _validation_revision_pattern = re.compile(\"^[a-zA-Z0-9]{1,%s}$\" % _max_chars)\n\n    @staticmethod\n    def invalid_name_message(value, reference_token=None):\n        if len(value) > ConanName._max_chars:\n            reason = \"is too long. Valid names must contain at most %s characters.\"\\\n                     % ConanName._max_chars\n        elif len(value) < ConanName._min_chars:\n            reason = \"is too short. Valid names must contain at least %s characters.\"\\\n                     % ConanName._min_chars\n        else:\n            reason = (\"is an invalid name. Valid names MUST begin with a \"\n                      \"letter, number or underscore, have between %s-%s chars, including \"\n                      \"letters, numbers, underscore, dot and dash\"\n                      % (ConanName._min_chars, ConanName._max_chars))\n        message = \"Value provided{ref_token}, '{value}' (type {type}), {reason}\".format(\n            ref_token=\" for {}\".format(reference_token) if reference_token else \"\",\n            value=value, type=type(value).__name__, reason=reason\n        )\n        raise InvalidNameException(message)\n\n    @staticmethod\n    def validate_string(value, reference_token=None):\n        \"\"\"Check for string\"\"\"\n        if not isinstance(value, string_types):\n            message = \"Value provided{ref_token}, '{value}' (type {type}), {reason}\".format(\n                ref_token=\" for {}\".format(reference_token) if reference_token else \"\",\n                value=value, type=type(value).__name__,\n                reason=\"is not a string\"\n            )\n            raise InvalidNameException(message)\n\n    @staticmethod\n    def validate_name(name, version=False, reference_token=None):\n        \"\"\"Check for name compliance with pattern rules\"\"\"\n        ConanName.validate_string(name, reference_token=reference_token)\n        if name == \"*\":\n            return\n        if ConanName._validation_pattern.match(name) is None:\n            if version and name.startswith(\"[\") and name.endswith(\"]\"):\n                return\n            ConanName.invalid_name_message(name, reference_token=reference_token)\n\n    @staticmethod\n    def validate_revision(revision):\n        if ConanName._validation_revision_pattern.match(revision) is None:\n            raise InvalidNameException(\"The revision field, must contain only letters \"\n                                       \"and numbers with a length between 1 and \"\n                                       \"%s\" % ConanName._max_chars)\n\n\nclass ConanFileReference(namedtuple(\"ConanFileReference\", \"name version user channel revision\")):\n    \"\"\" Full reference of a package recipes, e.g.:\n    opencv/2.4.10@lasote/testing\n    \"\"\"\n    whitespace_pattern = re.compile(r\"\\s+\")\n    sep_pattern = re.compile(\"@|/|#\")\n\n    def __new__(cls, name, version, user, channel, revision=None, validate=True):\n        \"\"\"Simple name creation.\n        @param name:        string containing the desired name\n        @param version:     string containing the desired version\n        @param user:        string containing the user name\n        @param channel:     string containing the user channel\n        @param revision:    string containing the revision (optional)\n        \"\"\"\n        version = Version(version)\n        obj = super(cls, ConanFileReference).__new__(cls, name, version, user, channel, revision)\n        if validate:\n            obj.validate()\n        return obj\n\n    def validate(self):\n        ConanName.validate_name(self.name, reference_token=\"package name\")\n        ConanName.validate_name(self.version, True, reference_token=\"package version\")\n        ConanName.validate_name(self.user, reference_token=\"user name\")\n        ConanName.validate_name(self.channel, reference_token=\"channel\")\n        if self.revision:\n            ConanName.validate_revision(self.revision)\n\n    def __hash__(self):\n        return hash((self.name, self.version, self.user, self.channel, self.revision))\n\n    @staticmethod\n    def loads(text, validate=True):\n        \"\"\" Parses a text string to generate a ConanFileReference object\n        \"\"\"\n        text = ConanFileReference.whitespace_pattern.sub(\"\", text)\n        tokens = ConanFileReference.sep_pattern.split(text)\n        try:\n            if len(tokens) not in (4, 5):\n                raise ValueError\n            name, version, user, channel = tokens[0:4]\n            revision = tokens[4] if len(tokens) == 5 else None\n        except ValueError:\n            raise ConanException(\"Wrong package recipe reference %s\\nWrite something like \"\n                                 \"OpenCV/1.0.6@user/stable\" % text)\n        obj = ConanFileReference(name, version, user, channel, revision, validate=validate)\n        return obj\n\n    def __repr__(self):\n        return \"%s/%s@%s/%s\" % (self.name, self.version, self.user, self.channel)\n\n    def full_repr(self):\n        str_rev = \"#%s\" % self.revision if self.revision else \"\"\n        return \"%s%s\" % (str(self), str_rev)\n\n    def dir_repr(self):\n        return \"/\".join(self[:-1])\n\n    def copy_with_rev(self, revision):\n        return ConanFileReference(self.name, self.version, self.user, self.channel, revision)\n\n    def copy_clear_rev(self):\n        return ConanFileReference(self.name, self.version, self.user, self.channel, None)\n\n\nclass PackageReference(namedtuple(\"PackageReference\", \"conan package_id revision\")):\n    \"\"\" Full package reference, e.g.:\n    opencv/2.4.10@lasote/testing, fe566a677f77734ae\n    \"\"\"\n\n    def __new__(cls, conan, package_id, revision=None, validate=True):\n        if \"#\" in package_id:\n            package_id, revision = package_id.rsplit(\"#\", 1)\n        obj = super(cls, PackageReference).__new__(cls, conan, package_id, revision)\n        if validate:\n            obj.validate()\n        return obj\n\n    def validate(self):\n        if self.revision:\n            ConanName.validate_revision(self.revision)\n\n    @staticmethod\n    def loads(text, validate=True):\n        text = text.strip()\n        tmp = text.split(\":\")\n        try:\n            conan = ConanFileReference.loads(tmp[0].strip())\n            package_id = tmp[1].strip()\n        except IndexError:\n            raise ConanException(\"Wrong package reference  %s\" % text)\n        return PackageReference(conan, package_id, validate=validate)\n\n    def __repr__(self):\n        return \"%s:%s\" % (self.conan, self.package_id)\n\n    def full_repr(self):\n        str_rev = \"#%s\" % self.revision if self.revision else \"\"\n        tmp = \"%s:%s%s\" % (self.conan.full_repr(), self.package_id, str_rev)\n        return tmp\n\n    def copy_with_revs(self, revision, p_revision):\n        return PackageReference(self.conan.copy_with_rev(revision), self.package_id, p_revision)\n\n    def copy_clear_rev(self):\n        ref = self.conan.copy_clear_rev()\n        return PackageReference(ref, self.package_id, revision=None)\n", "path": "conans/model/ref.py"}], "after_files": [{"content": "from collections import namedtuple\n\nimport re\nfrom six import string_types\n\nfrom conans.errors import ConanException, InvalidNameException\nfrom conans.model.version import Version\n\n\ndef check_valid_ref(ref, allow_pattern):\n    try:\n        if not isinstance(ref, ConanFileReference):\n            ref = ConanFileReference.loads(ref, validate=True)\n        return \"*\" not in ref or allow_pattern\n    except ConanException:\n        pass\n    return False\n\n\nclass ConanName(object):\n    _max_chars = 51\n    _min_chars = 2\n    _validation_pattern = re.compile(\"^[a-zA-Z0-9_][a-zA-Z0-9_\\+\\.-]{%s,%s}$\"\n                                     % (_min_chars - 1, _max_chars - 1))\n\n    _validation_revision_pattern = re.compile(\"^[a-zA-Z0-9]{1,%s}$\" % _max_chars)\n\n    @staticmethod\n    def invalid_name_message(value, reference_token=None):\n        if len(value) > ConanName._max_chars:\n            reason = \"is too long. Valid names must contain at most %s characters.\"\\\n                     % ConanName._max_chars\n        elif len(value) < ConanName._min_chars:\n            reason = \"is too short. Valid names must contain at least %s characters.\"\\\n                     % ConanName._min_chars\n        else:\n            reason = (\"is an invalid name. Valid names MUST begin with a \"\n                      \"letter, number or underscore, have between %s-%s chars, including \"\n                      \"letters, numbers, underscore, dot and dash\"\n                      % (ConanName._min_chars, ConanName._max_chars))\n        message = \"Value provided{ref_token}, '{value}' (type {type}), {reason}\".format(\n            ref_token=\" for {}\".format(reference_token) if reference_token else \"\",\n            value=value, type=type(value).__name__, reason=reason\n        )\n        raise InvalidNameException(message)\n\n    @staticmethod\n    def validate_string(value, reference_token=None):\n        \"\"\"Check for string\"\"\"\n        if not isinstance(value, string_types):\n            message = \"Value provided{ref_token}, '{value}' (type {type}), {reason}\".format(\n                ref_token=\" for {}\".format(reference_token) if reference_token else \"\",\n                value=value, type=type(value).__name__,\n                reason=\"is not a string\"\n            )\n            raise InvalidNameException(message)\n\n    @staticmethod\n    def validate_name(name, version=False, reference_token=None):\n        \"\"\"Check for name compliance with pattern rules\"\"\"\n        ConanName.validate_string(name, reference_token=reference_token)\n        if name == \"*\":\n            return\n        if ConanName._validation_pattern.match(name) is None:\n            if version and name.startswith(\"[\") and name.endswith(\"]\"):\n                return\n            ConanName.invalid_name_message(name, reference_token=reference_token)\n\n    @staticmethod\n    def validate_revision(revision):\n        if ConanName._validation_revision_pattern.match(revision) is None:\n            raise InvalidNameException(\"The revision field, must contain only letters \"\n                                       \"and numbers with a length between 1 and \"\n                                       \"%s\" % ConanName._max_chars)\n\n\nclass ConanFileReference(namedtuple(\"ConanFileReference\", \"name version user channel revision\")):\n    \"\"\" Full reference of a package recipes, e.g.:\n    opencv/2.4.10@lasote/testing\n    \"\"\"\n    whitespace_pattern = re.compile(r\"\\s+\")\n    sep_pattern = re.compile(r\"([^/]+)/([^/]+)@([^/]+)/([^/#]+)#?(.+)?\")\n\n    def __new__(cls, name, version, user, channel, revision=None, validate=True):\n        \"\"\"Simple name creation.\n        @param name:        string containing the desired name\n        @param version:     string containing the desired version\n        @param user:        string containing the user name\n        @param channel:     string containing the user channel\n        @param revision:    string containing the revision (optional)\n        \"\"\"\n        version = Version(version)\n        obj = super(cls, ConanFileReference).__new__(cls, name, version, user, channel, revision)\n        if validate:\n            obj.validate()\n        return obj\n\n    def validate(self):\n        ConanName.validate_name(self.name, reference_token=\"package name\")\n        ConanName.validate_name(self.version, True, reference_token=\"package version\")\n        ConanName.validate_name(self.user, reference_token=\"user name\")\n        ConanName.validate_name(self.channel, reference_token=\"channel\")\n        if self.revision:\n            ConanName.validate_revision(self.revision)\n\n    def __hash__(self):\n        return hash((self.name, self.version, self.user, self.channel, self.revision))\n\n    @staticmethod\n    def loads(text, validate=True):\n        \"\"\" Parses a text string to generate a ConanFileReference object\n        \"\"\"\n        text = ConanFileReference.whitespace_pattern.sub(\"\", text)\n        try:\n            # Split returns empty start and end groups\n            _, name, version, user, channel, revision, _ = ConanFileReference.sep_pattern.split(text)\n        except ValueError:\n            raise ConanException(\"Wrong package recipe reference %s\\nWrite something like \"\n                                 \"OpenCV/1.0.6@user/stable\" % text)\n        obj = ConanFileReference(name, version, user, channel, revision, validate=validate)\n        return obj\n\n    def __repr__(self):\n        return \"%s/%s@%s/%s\" % (self.name, self.version, self.user, self.channel)\n\n    def full_repr(self):\n        str_rev = \"#%s\" % self.revision if self.revision else \"\"\n        return \"%s%s\" % (str(self), str_rev)\n\n    def dir_repr(self):\n        return \"/\".join(self[:-1])\n\n    def copy_with_rev(self, revision):\n        return ConanFileReference(self.name, self.version, self.user, self.channel, revision)\n\n    def copy_clear_rev(self):\n        return ConanFileReference(self.name, self.version, self.user, self.channel, None)\n\n\nclass PackageReference(namedtuple(\"PackageReference\", \"conan package_id revision\")):\n    \"\"\" Full package reference, e.g.:\n    opencv/2.4.10@lasote/testing, fe566a677f77734ae\n    \"\"\"\n\n    def __new__(cls, conan, package_id, revision=None, validate=True):\n        if \"#\" in package_id:\n            package_id, revision = package_id.rsplit(\"#\", 1)\n        obj = super(cls, PackageReference).__new__(cls, conan, package_id, revision)\n        if validate:\n            obj.validate()\n        return obj\n\n    def validate(self):\n        if self.revision:\n            ConanName.validate_revision(self.revision)\n\n    @staticmethod\n    def loads(text, validate=True):\n        text = text.strip()\n        tmp = text.split(\":\")\n        try:\n            conan = ConanFileReference.loads(tmp[0].strip())\n            package_id = tmp[1].strip()\n        except IndexError:\n            raise ConanException(\"Wrong package reference  %s\" % text)\n        return PackageReference(conan, package_id, validate=validate)\n\n    def __repr__(self):\n        return \"%s:%s\" % (self.conan, self.package_id)\n\n    def full_repr(self):\n        str_rev = \"#%s\" % self.revision if self.revision else \"\"\n        tmp = \"%s:%s%s\" % (self.conan.full_repr(), self.package_id, str_rev)\n        return tmp\n\n    def copy_with_revs(self, revision, p_revision):\n        return PackageReference(self.conan.copy_with_rev(revision), self.package_id, p_revision)\n\n    def copy_clear_rev(self):\n        ref = self.conan.copy_clear_rev()\n        return PackageReference(ref, self.package_id, revision=None)\n", "path": "conans/model/ref.py"}]}