Repository: python-poetry/poetry

Issue:
poetry search fails with ValueError: Unable to parse ".000001".
<!--
  Hi there! Thank you for discovering and submitting an issue.

  Before you submit this; let's make sure of a few things.
  Please make sure the following boxes are ticked if they are correct.
  If not, please try and fulfill these first.
-->

<!-- Checked checkbox should look like this: [x] -->
- [x] I am on the [latest](https://github.com/sdispater/poetry/releases/latest) Poetry version.
- [x] I have searched the [issues](https://github.com/sdispater/poetry/issues) of this repo and believe that this is not a duplicate.
- [x] If an exception occurs when executing a command, I executed it again in debug mode (`-vvv` option).

<!--
  Once those are done, if you're able to fill in the following list with your information,
  it'd be very helpful to whoever handles the issue.
-->

- **OS version and name**: archlinux
- **Poetry version**: 0.12.8

## Issue
<!-- Now feel free to write your issue, but please be descriptive! Thanks again 🙌 ❤️ -->
When I try to run `poetry search <name of my package>`, I get an error:
```
[ValueError]   
Unable to parse ".000001".  
                              
Exception trace:
 /home/charles/.poetry/lib/poetry/_vendor/py3.7/cleo/application.py in run() at line 94
   status_code = self.do_run(input_, output_)
 /home/charles/.poetry/lib/poetry/console/application.py in do_run() at line 88
   return super(Application, self).do_run(i, o)
 /home/charles/.poetry/lib/poetry/_vendor/py3.7/cleo/application.py in do_run() at line 197
   status_code = command.run(input_, output_)
 /home/charles/.poetry/lib/poetry/console/commands/command.py in run() at line 77
   return super(BaseCommand, self).run(i, o)
 /home/charles/.poetry/lib/poetry/_vendor/py3.7/cleo/commands/base_command.py in run() at line 146
   status_code = self.execute(input_, output_)
 /home/charles/.poetry/lib/poetry/_vendor/py3.7/cleo/commands/command.py in execute() at line 107
   return self.handle()
 /home/charles/.poetry/lib/poetry/console/commands/search.py in handle() at line 20
   results = PyPiRepository().search(self.argument("tokens"), flags)
 /home/charles/.poetry/lib/poetry/repositories/pypi_repository.py in search() at line 207
   result = Package(hit["name"], hit["version"], hit["version"])
 /home/charles/.poetry/lib/poetry/packages/package.py in __init__() at line 40
   self._version = Version.parse(version)
 /home/charles/.poetry/lib/poetry/semver/version.py in parse() at line 200
   raise ValueError('Unable to parse "{}".'.format(text))

search [-N|--only-name] [--] <tokens> (<tokens>)...
```
The package I'm searching is mine, and is on a private repository. The problem is probably related to my package, since `poetry search` otherwise works with other packages(existing or not).


Discussion:
From the error you see, Poetry tries to parse a `.000001` version which is not valid.
Right, and where does this version come from? My package version(the one I'm trying to install) is "0.0.1".
The `search` command does not search in private repositories, only on PyPI. So the package you are trying to search must exist on PyPI with a bad version.
Ok, well I assure you that it's not the case. `pip search my-package-name` confirms that. The package name is `kioto-basic-utils`, if you want to try for yourself.
I have run into the same error on poetry version 0.12.11: Running the command `poetry search beautifulsoup -vvv` crashes with the error:

```
[ParseVersionError]
Unable to parse "rev452".

Exception trace:
 /home/peter/.poetry/lib/poetry/_vendor/py3.7/cleo/application.py in run() at line 94
   status_code = self.do_run(input_, output_)
 /home/peter/.poetry/lib/poetry/console/application.py in do_run() at line 88
   return super(Application, self).do_run(i, o)
 /home/peter/.poetry/lib/poetry/_vendor/py3.7/cleo/application.py in do_run() at line 197
   status_code = command.run(input_, output_)
 /home/peter/.poetry/lib/poetry/console/commands/command.py in run() at line 77
   return super(BaseCommand, self).run(i, o)
 /home/peter/.poetry/lib/poetry/_vendor/py3.7/cleo/commands/base_command.py in run() at line 146
   status_code = self.execute(input_, output_)
 /home/peter/.poetry/lib/poetry/_vendor/py3.7/cleo/commands/command.py in execute() at line 107
   return self.handle()
 /home/peter/.poetry/lib/poetry/console/commands/search.py in handle() at line 20
   results = PyPiRepository().search(self.argument("tokens"), flags)
 /home/peter/.poetry/lib/poetry/repositories/pypi_repository.py in search() at line 221
   result = Package(hit["name"], hit["version"], hit["version"])
 /home/peter/.poetry/lib/poetry/packages/package.py in __init__() at line 40
   self._version = Version.parse(version)
 /home/peter/.poetry/lib/poetry/semver/version.py in parse() at line 201
   raise ParseVersionError('Unable to parse "{}".'.format(text))

```

Seems like that command is quite brittle and crashes on packages using custom version strings. In this case, the package `ElementSoup` seems to be the culprit.
@sdispater while I agree that `.000001` is not a PEP 440 compliant version, it is still possible for packages on PyPi to have such invalid version strings, and it is unacceptable for poetry to just break when one pops up. I propose that packages with invalid versions should just be ignored and left out of the search results, which is also what PEP 440 recommends: 

`Installation tools SHOULD ignore any public versions which do not comply with this scheme but MUST also include the normalizations specified below.`
This bug is still not fixed in the newest release candidate 1.0.0b1.

Resolving patch:
diff --git a/poetry/repositories/pypi_repository.py b/poetry/repositories/pypi_repository.py
--- a/poetry/repositories/pypi_repository.py
+++ b/poetry/repositories/pypi_repository.py
@@ -221,9 +221,17 @@ def search(self, query, mode=0):
         hits = client.search(search, "or")
 
         for hit in hits:
-            result = Package(hit["name"], hit["version"], hit["version"])
-            result.description = to_str(hit["summary"])
-            results.append(result)
+            try:
+                result = Package(hit["name"], hit["version"], hit["version"])
+                result.description = to_str(hit["summary"])
+                results.append(result)
+            except ParseVersionError:
+                self._log(
+                    'Unable to parse version "{}" for the {} package, skipping'.format(
+                        hit["version"], hit["name"]
+                    ),
+                    level="debug",
+                )
 
         return results
 


Test patch:
diff --git a/tests/repositories/fixtures/pypi.org/json/pygame-music-grid.json b/tests/repositories/fixtures/pypi.org/json/pygame-music-grid.json
new file mode 100644
--- /dev/null
+++ b/tests/repositories/fixtures/pypi.org/json/pygame-music-grid.json
@@ -0,0 +1,81 @@
+{
+    "info":{
+        "author":"eric dexter",
+        "author_email":"irc.dexter@gmail.com",
+        "bugtrack_url":null,
+        "classifiers":[
+            "Topic :: Multimedia :: Sound/Audio :: Editors"
+        ],
+        "description":"a clickable grid for drum machines, piano rolls that is customizble from an init \r\nfile)or will be) that will include the script to be ran when a definable button is \r\nhit  written in pygame and tested with python 2.5",
+        "description_content_type":null,
+        "docs_url":null,
+        "download_url":"http://www.ziddu.com/download/5498230/pygamepianorollbeta.90.zip.html",
+        "downloads":{
+            "last_day":-1,
+            "last_month":-1,
+            "last_week":-1
+        },
+        "home_page":"http://dexrowem.blogspot.com/search?q=pygame+music+grid",
+        "keywords":"python, pygame, drum machine, piano roll",
+        "license":"",
+        "maintainer":"",
+        "maintainer_email":"",
+        "name":"pygame-music-grid",
+        "package_url":"https://pypi.org/project/pygame-music-grid/",
+        "platform":"",
+        "project_url":"https://pypi.org/project/pygame-music-grid/",
+        "project_urls":{
+            "Download":"http://www.ziddu.com/download/5498230/pygamepianorollbeta.90.zip.html",
+            "Homepage":"http://dexrowem.blogspot.com/search?q=pygame+music+grid"
+        },
+        "release_url":"https://pypi.org/project/pygame-music-grid/.9/",
+        "requires_dist":null,
+        "requires_python":null,
+        "summary":"a grid for music programs",
+        "version":".9"
+    },
+    "last_serial":710340,
+    "releases":{
+        ".9":[
+          {
+            "comment_text": "",
+            "digests": {
+              "md5": "76e2c2e8adea20377d9a7e6b6713c952",
+              "sha256": "8d6d96001aa7f0a6a4a95e8143225b5d06e41b1131044913fecb8f85a125714b"
+            },
+            "downloads": -1,
+            "filename": "PyYAML-4.2b4-cp27-cp27m-win32.whl",
+            "has_sig": false,
+            "md5_digest": "76e2c2e8adea20377d9a7e6b6713c952",
+            "packagetype": "bdist_wheel",
+            "python_version": "cp27",
+            "requires_python": null,
+            "size": 104988,
+            "upload_time": "2018-07-02T03:17:55",
+            "url": "https://files.pythonhosted.org/packages/12/9b/efdbaa3c9694b6315a4410e0d494ad50c5ade22ce33f4b482bfaea3930fd/PyYAML-4.2b4-cp27-cp27m-win32.whl"
+          }
+        ],
+        "1.0":[
+          {
+            "comment_text": "",
+            "digests": {
+              "md5": "a83441aa7004e474bed6f6daeb61f27a",
+              "sha256": "d5eef459e30b09f5a098b9cea68bebfeb268697f78d647bd255a085371ac7f3f"
+            },
+            "downloads": -1,
+            "filename": "PyYAML-3.13-cp27-cp27m-win32.whl",
+            "has_sig": false,
+            "md5_digest": "a83441aa7004e474bed6f6daeb61f27a",
+            "packagetype": "bdist_wheel",
+            "python_version": "cp27",
+            "requires_python": null,
+            "size": 191712,
+            "upload_time": "2018-07-05T22:53:15",
+            "url": "https://files.pythonhosted.org/packages/b8/2e/9c2285870c9de070a1fa5ede702ab5fb329901b3cc4028c24f44eda27c5f/PyYAML-3.13-cp27-cp27m-win32.whl"
+          }
+        ]
+    },
+    "urls":[
+
+    ]
+}
diff --git a/tests/repositories/test_pypi_repository.py b/tests/repositories/test_pypi_repository.py
--- a/tests/repositories/test_pypi_repository.py
+++ b/tests/repositories/test_pypi_repository.py
@@ -173,3 +173,15 @@ def test_pypi_repository_supports_reading_bz2_files():
         assert expected_extras[name] == sorted(
             package.extras[name], key=lambda r: r.name
         )
+
+
+def test_invalid_versions_ignored():
+    repo = MockRepository()
+
+    # the json metadata for this package contains one malformed version
+    # and a correct one.
+    packages = repo.find_packages("pygame-music-grid")
+    assert len(packages) == 1
+
+    # This should just not raise
+    repo.search("pygame-music-grid")