Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Potential thread safety issue under certain environments: Incorrectly nested style tag found #3010

Closed
abn opened this issue Sep 30, 2020 · 33 comments
Assignees
Labels
kind/bug Something isn't working as expected

Comments

@abn
Copy link
Member

abn commented Sep 30, 2020

In certain environments, specifically CI environments, this issue occurs randomly. It would seem that when using the new executor for installation the io being used is not thread safe and pastel fails attempting to pop the wrong stack.

  • Installing glob2 (0.6)

ValueError

Incorrectly nested style tag found.

  • Installing httpretty (0.9.7)
@abn abn added kind/bug Something isn't working as expected status/triage This issue needs to be triaged labels Sep 30, 2020
@PetterS
Copy link
Contributor

PetterS commented Oct 5, 2020

There are also other issues with the parallel installation: see #2658.

brechtm added a commit to brechtm/rinohtype that referenced this issue Oct 7, 2020
... until the issues with 1.1.x have been resolved.

See python-poetry/poetry#3010
@machielg
Copy link

machielg commented Oct 15, 2020

I also have this problem when doing poetry (1.1.2) install in gitlab. The workaround is to disable the new installer: poetry config experimental.new-installer false

it seems to fail at a random point or sometimes not at all.

@PetterS
Copy link
Contributor

PetterS commented Oct 15, 2020

Yeah, this should be relatively easy to fix. Just wrap a lot of calls to self._io with a lock in the executor. Alteratively, make IO from clikit safe to call from multiple threads.

@sdispater sdispater self-assigned this Oct 30, 2020
@vikigenius
Copy link

Yeah, I am also facing this issue on aws, when using the user-data script to install poetry packages

@PetterS
Copy link
Contributor

PetterS commented Dec 14, 2020

For automation or when reliability is needed, the new installer should definitely be disabled.

We have a config file in the repo disabling it by default. If anyone wants to use it (e.g. when setting up a new env), they can do so manually.

@michalszelagsonos
Copy link

I hit this couple times now, also in CI. If I keep running into this, I may disable new installer which is a bummer since it significantly speeds up the install in my project with 100+ dependencies.

@sdispater
Copy link
Member

@Michal-Szelag-Sonos Which Python version are you using when the error occurs?

@michalszelagsonos
Copy link

@Michal-Szelag-Sonos Which Python version are you using when the error occurs?

I am seeing this on 2.7

@PetterS
Copy link
Contributor

PetterS commented Dec 18, 2020

Should happen on any Python version since IO is not thread-safe. This particular issue is easy to fix in the executor, though. There are other concurrency issues that are more tricky.

@sdispater
Copy link
Member

@Michal-Szelag-Sonos Which Python version are you using when the error occurs?

I am seeing this on 2.7

Yes, from experience I only have seen this on Python 2.7 and not on other Python versions, I am not sure why. I don't know if it's a coincidence or not though.

It may also be caused by the futures package used on Python 2.7.

Note that every write in the Executor class occurs after having acquired a lock so even though the IO is not thread safe, everything should work as expected.

@michalszelagsonos
Copy link

So, silly question, can I use python3 poetry to manage a python2.7 project, assuming I would install poetry using pip3? It seems that this should work but double checking. I could test the 2.7 vs 3.x assertion by using python3 env for my toolchain which manages python 2.7 project and give it a few days to bake in.

@PetterS
Copy link
Contributor

PetterS commented Dec 18, 2020

Note that every write in the Executor class occurs after having acquired a lock so even though the IO is not thread safe, everything should work as expected.

Right, but I think some places are missing a lock, like this one:

self._io.write_line(

@sinoroc
Copy link

sinoroc commented Dec 18, 2020

So, silly question, can I use python3 poetry to manage a python2.7 project, assuming I would install poetry using pip3? It seems that this should work but double checking. I could test the 2.7 vs 3.x assertion by using python3 env for my toolchain which manages python 2.7 project and give it a few days to bake in.

If I am not mistaken, that should work.

@Michal-Szelag-Sonos It seems like you are not installing with get-poetry.py, so you should be safe from this. But for those who do install with get-poetry.py, I would recommend reading this ticket if you encounter issues: #3288.

@michalszelagsonos
Copy link

michalszelagsonos commented May 10, 2021

Quick update, upgraded our CI container to python3.8 with poetry 1.1.6 and I am hitting this issue about 40% of the time. With such a high failure rate, this negates the 1.1.x performance improvement. Unfortunately I have to disable the new installer as a result of this, which is a bummer since our project has around 170 dependencies so the new installer was really shaving a few minutes off each run.

EDIT: I made a mistake in my original report. Turns out I wasn't running with 3.8, more info down below.

@abn
Copy link
Member Author

abn commented May 10, 2021

This could be triggered due to resource limits on the container. I wonder if this s python issue under constrained environments.

@Michal-Szelag-Sonos can you provide some additional details regarding the environment, container etc? Can you create an example where we could try reproducing this under 3.8?

@dev-zero
Copy link

Just hit this on a CI run on GHA on one of my projects with Pypy-3.6. Here's the full build log.

@abn
Copy link
Member Author

abn commented May 11, 2021

Hmm. Looks like 3.7 works. Does anyone have this issue on python >= 3.7? (realised above mention is of 3.8)

@dev-zero does using poetry from master make a difference?

@dev-zero
Copy link

dev-zero commented May 11, 2021

seems ok so far: unable to trigger in 5 runs of the complete workflow. On the other hand I was not able to trigger it in a slightly different PR with version 1.1.6.

@michalszelagsonos
Copy link

I had another look at my environment and looks like I mis-spoke earlier. I upgraded one of the containers, but not the poetry one. I finished the CI upgrade environment using python3.8 and poetry 1.1.6, I also added explicit venv creation to the job since that mimics our dev environment and now I cannot reproduce this issue. I would previously hit it almost half the time, I ran it over 10 times and no failures. So, to recap, I changed 2 things:

  • I upgraded to python3.8
  • I switched to creating venv myself and having poetry using it rather than poetry making one on its own

I also tried to replicate this issue using docker on my local system, by limiting CPU and memory resources and I cannot repro this using python2.7. Our CI runs on a kubernetes cluster, backed by cloud infrastructure so I have a feeling that the behaviors on that system are different enough such that it triggers this problem. I also tried limiting IOPs on the storage driver since the issue may be related to how that subsystem behaves but I've had no luck reproducing it. This may also be related to the kernel scheduler itself which I cannot reproduce locally as well. Hard to say for sure.

paxcodes added a commit to paxcodes/portfolio_api that referenced this issue Jun 4, 2021
Doing poetry install causes CPU usage to reach ~100% for an instance that has only one CPU.

Possible relevant issues
python-poetry/poetry#3756
python-poetry/poetry#3010
@michalszelagsonos
Copy link

michalszelagsonos commented Feb 21, 2022

Reviving this issue since we're seeing what looks like a locking problem or poetry is making the wrong call on the dependency install form time to time. We're using python3.8 for poetry so this isn't a 2.7 related problem. During install, poetry sometimes fails due to missing dist-info and it appears to be looking for the wrong version. Below is the output from a CI run where it fails to install traitlets while pygments is downgrading from 2.11.2 to 2.5.2. Note the error expecting dist-info from 2.11.2.

The pyproject.toml from the project is below. Note, this install runs in a docker container using python:3.8-buster image.
Since the install occurs in the container, we disable virtualenv creation first with:

poetry config virtualenvs.create false

Error log:

14:36:16  #12 [stage-0 7/9] RUN poetry install
14:36:17  #12 1.017 Skipping virtualenv creation, as specified in config file.
22:15:40  #11 1.212 Installing dependencies from lock file
22:15:40  #11 1.565 
22:15:40  #11 1.565 Package operations: 22 installs, 4 updates, 3 removals
22:15:40  #11 1.565 
22:15:40  #11 1.566   • Removing importlib-metadata (4.8.2)
22:15:41  #11 1.972   • Removing typing-extensions (4.0.0)
22:15:42  #11 2.604   • Removing zipp (3.6.0)
22:15:42  #11 3.156   • Installing jmespath (0.10.0)
22:15:42  #11 3.158   • Installing python-dateutil (2.8.2)
22:15:42  #11 3.158   • Updating urllib3 (1.26.8 -> 1.26.7)
22:15:43  #11 4.325   • Installing botocore (1.23.8)
22:15:43  #11 4.328   • Updating pyparsing (3.0.7 -> 3.0.6)
22:15:45  #11 6.150   • Installing attrs (21.2.0)
22:15:45  #11 6.153   • Installing decorator (5.1.0)
22:15:45  #11 6.156   • Installing ipython-genutils (0.2.0)
22:15:45  #11 6.158   • Updating packaging (20.9 -> 21.3)
22:15:45  #11 6.162   • Installing iniconfig (1.1.1)
22:15:45  #11 6.167   • Installing py (1.11.0)
22:15:45  #11 6.169   • Installing s3transfer (0.5.0)
22:15:45  #11 6.173   • Installing wcwidth (0.2.5)
22:15:45  #11 6.174   • Installing pluggy (1.0.0)
22:15:46  #11 7.531   • Installing boto3 (1.20.8)
22:15:46  #11 7.533   • Installing pickleshare (0.7.5)
22:15:46  #11 7.535   • Installing prompt-toolkit (1.0.18)
22:15:46  #11 7.537   • Updating pygments (2.11.2 -> 2.5.2)
22:15:46  #11 7.539   • Installing pytest (6.2.5)
22:15:46  #11 7.542   • Installing pyyaml (5.4.1)
22:15:46  #11 7.545   • Installing simplegeneric (0.8.1)
22:15:46  #11 7.547   • Installing traitlets (4.3.3)
22:15:47  #11 8.664 
22:15:47  #11 8.664   EnvCommandError
22:15:47  #11 8.664 
22:15:47  #11 8.664   Command ['/usr/local/bin/python', '-m', 'pip', 'install', '--no-deps', '/root/.cache/pypoetry/artifacts/c8/9d/8a/0ac7b9700197ce07d35f1f4db4c2afa79c83149e7ad6fafd4e82aa1143/traitlets-4.3.3-py2.py3-none-any.whl'] errored with the following return code 1, and output: 
22:15:47  #11 8.664   Processing /root/.cache/pypoetry/artifacts/c8/9d/8a/0ac7b9700197ce07d35f1f4db4c2afa79c83149e7ad6fafd4e82aa1143/traitlets-4.3.3-py2.py3-none-any.whl
22:15:47  #11 8.664   Installing collected packages: traitlets
22:15:47  #11 8.664   Successfully installed traitlets-4.3.3
22:15:47  #11 8.664   Traceback (most recent call last):
22:15:47  #11 8.664     File "/usr/local/lib/python3.8/runpy.py", line 194, in _run_module_as_main
22:15:47  #11 8.664       return _run_code(code, main_globals, None,
22:15:47  #11 8.664     File "/usr/local/lib/python3.8/runpy.py", line 87, in _run_code
22:15:47  #11 8.664       exec(code, run_globals)
22:15:47  #11 8.664     File "/usr/local/lib/python3.8/site-packages/pip/__main__.py", line 26, in <module>
22:15:47  #11 8.664       sys.exit(_main())
22:15:47  #11 8.664     File "/usr/local/lib/python3.8/site-packages/pip/_internal/cli/main.py", line 75, in main
22:15:47  #11 8.664       return command.main(cmd_args)
22:15:47  #11 8.664     File "/usr/local/lib/python3.8/site-packages/pip/_internal/cli/base_command.py", line 121, in main
22:15:47  #11 8.664       return self._main(args)
22:15:47  #11 8.664     File "/usr/local/lib/python3.8/site-packages/pip/_internal/cli/base_command.py", line 265, in _main
22:15:47  #11 8.664       self.handle_pip_version_check(options)
22:15:47  #11 8.664     File "/usr/local/lib/python3.8/site-packages/pip/_internal/cli/req_command.py", line 155, in handle_pip_version_check
22:15:47  #11 8.664       pip_self_version_check(session, options)
22:15:47  #11 8.664     File "/usr/local/lib/python3.8/site-packages/pip/_internal/self_outdated_check.py", line 130, in pip_self_version_check
22:15:47  #11 8.664       installed_version = get_installed_version("pip")
22:15:47  #11 8.664     File "/usr/local/lib/python3.8/site-packages/pip/_internal/utils/misc.py", line 665, in get_installed_version
22:15:47  #11 8.664       working_set = pkg_resources.WorkingSet()
22:15:47  #11 8.664     File "/usr/local/lib/python3.8/site-packages/pip/_vendor/pkg_resources/__init__.py", line 567, in __init__
22:15:47  #11 8.664       self.add_entry(entry)
22:15:47  #11 8.664     File "/usr/local/lib/python3.8/site-packages/pip/_vendor/pkg_resources/__init__.py", line 623, in add_entry
22:15:47  #11 8.664       for dist in find_distributions(entry, True):
22:15:47  #11 8.664     File "/usr/local/lib/python3.8/site-packages/pip/_vendor/pkg_resources/__init__.py", line 2065, in find_on_path
22:15:47  #11 8.664       for dist in factory(fullpath):
22:15:47  #11 8.664     File "/usr/local/lib/python3.8/site-packages/pip/_vendor/pkg_resources/__init__.py", line 2127, in distributions_from_metadata
22:15:47  #11 8.664       if len(os.listdir(path)) == 0:
22:15:47  #11 8.664   FileNotFoundError: [Errno 2] No such file or directory: '/usr/local/lib/python3.8/site-packages/Pygments-2.11.2.dist-info'
22:15:47  #11 8.664   
22:15:47  #11 8.664 
22:15:47  #11 8.664   at /usr/local/lib/python3.8/site-packages/poetry/utils/env.py:1075 in _run
22:15:47  #11 8.755       1071│                 output = subprocess.check_output(
22:15:47  #11 8.755       1072│                     cmd, stderr=subprocess.STDOUT, **kwargs
22:15:47  #11 8.756       1073│                 )
22:15:47  #11 8.756       1074│         except CalledProcessError as e:
22:15:47  #11 8.757     → 1075│             raise EnvCommandError(e, input=input_)
22:15:47  #11 8.757       1076│ 
22:15:47  #11 8.758       1077│         return decode(output)
22:15:47  #11 8.758       1078│ 
22:15:47  #11 8.758       1079│     def execute(self, bin, *args, **kwargs):

pyproject.toml:

[tool.poetry]
name = "sample"
version = "0.0.1"
description = "Example of a failing run"
authors = ["Foo Bar <[email protected]>"]

[tool.poetry.dependencies]
python = ">=3.6,<4.0"
configparser = "^5.0.2"
pytest = "^6.2.3"
"pytest.mock" = "^3.5.1"
boto3 = "^1.17.12"
pyyaml = "^5.4.1"

[tool.poetry.dev-dependencies]
ipython = "^5.10"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

@michalszelagsonos
Copy link

@abn Any thoughts on this? ⬆️

@jouve
Copy link
Contributor

jouve commented Mar 19, 2022

@abn
Copy link
Member Author

abn commented Mar 22, 2022

@michalszelagsonos the issue is likely due to the parallel execution of multiple pip instances. In your case pygments being updated means the existing directory was deleted during the update, but before that it was listed as an existing path in another instance of pip (installation of traitlets) executing a call find_distributions which then tries to list files in this path.

You can workaround this by setting workers to 1. Also, try updating pip.

PS: I would recommend not disabling virtual environment creation, as sharing the system environment with poetry and other installations will only cause headaches. Besides, having a venv should reduce the probability of this collission.

Further, 1.2 should also improve this situation by not relying on system environment pip versions.

@michalszelagsonos
Copy link

@abn Thanks for chiming in. If I set the installer.max-workers to 1, will that essentially negate the performance improvements from the new installer? Currently, we have disabled the new installer which serialized the dependency installation and worked around this problem. I am curious if limiting max workers but turning on the new installer is functionally different than what we have now. Thanks for the support and keep up the great work!

@abn
Copy link
Member Author

abn commented Mar 23, 2022

The installer is better in a few aspects outside of performance. Using installer.max-workers=1 would be what I recommend since the old one is due to be removed. And realistically, I do not think these edge cases can be handled at poetry level outside of a retry logic somewhere (which can lead to other issues) untill we drop pip as the wheel installer. That said, this particular issue, iirc has been fixed in newer versions of pip, so either doing poetry run python -m pip --upgrade pip or using 1.2 should resolve the issue. Feel free to reach out on discord if that helps.

@PetterS
Copy link
Contributor

PetterS commented Apr 2, 2022

max-workers=1 should be the default since correctness is very important

@abn
Copy link
Member Author

abn commented May 10, 2022

@PetterS since the install itself is something that is safely repeatable and in cases where you do want that extra guarantee it might be better to explicitly control the workers, I would say that the defaults are okay for now.

The reason I say this is that it is an active trade off, right now it seems that the cases where the current defaults fail are few and far between with 1.2. And the improvements that the new installer brings to the table for majority users far outweighs the correctness argument in this context.

I am going to close this issue for now since we have no real action here. However, if with 1.2 people are finding the reported issue, please open a new issue and link it to this one.

Friendly reminder that the old installer will be removed in a future release, use max worker configuration instead of disabling the installer.

@jeffwidman
Copy link

jeffwidman commented Aug 8, 2022

Looks like there will also be a installer.parallel false option that can be used in place of installer.max-workers 1.

However, neither is available with poetry < 1.2, (as I just discovered!) so AFAICT, experimental.new-installer false is the only viable workaround for now... plus once 1.2 drops, these workarounds shouldn't even be needed.

jeffwidman added a commit to jeffwidman/dependabot-core that referenced this issue Sep 13, 2022
The original intent of this code was to workaround a thread safety
issue. However, according to the `poetry` maintainer, the more "future
proof" workaround is [to limit the worker count rather than disable the
new installer](python-poetry/poetry#3010 (comment)).

The underlying root issue is supposedly resolved in [the upcoming `1.2`
release of `poetry`](python-poetry/poetry#3010 (comment)).

But until then let's use the cleaner workaround.
jeffwidman added a commit to dependabot/dependabot-core that referenced this issue Oct 7, 2022
This was added in
#3459 to workaround
the underlying issue of
python-poetry/poetry#3010.

However, `poetry` `1.2` fixed the underlying issue, and we are now on `poetry` `1.2.1` so we can remove this workaround.

See also:
* python-poetry/poetry#3010 (comment)
* python-poetry/poetry#3010 (comment)
* python-poetry/poetry#3010 (comment)
* #5492
jeffwidman added a commit to dependabot/dependabot-core that referenced this issue Nov 22, 2022
The associated ticket on Poetry
(python-poetry/poetry#3010) was closed because
the underlying triggers were mostly resolved in `poetry` `1.2` using the
new installer. In particular, see
python-poetry/poetry#3010 (comment).

We flipped to the new installer in
#5838, so this skip
should no longer be necessary.

This reverts commit ceb5700.

Additionally, this flips the fixture from a Python 2 to a Python 3
fixture. We dropped support for Python 2 a while ago, but didn't notice
this test needed updating because it was skipped.

Another test still needs a python 2 fixture to ensure that lockfiles
still pinned to python 2 throw the correct exception.

So copied the existing fixture, updated it to specify a python 3
version, and then regenerated the lockfile using:
```
PYENV_VERSION=3.10.7 pyenv exec poetry lock --no-update
```
jeffwidman added a commit to dependabot/dependabot-core that referenced this issue Nov 23, 2022
The associated ticket on Poetry
(python-poetry/poetry#3010) was closed because
the underlying triggers were mostly resolved in `poetry` `1.2` using the
new installer. In particular, see
python-poetry/poetry#3010 (comment).

We flipped to the new installer in
#5838, so this skip
should no longer be necessary.

This reverts commit ceb5700.

Additionally, this flips the fixture from a Python 2 to a Python 3
fixture. We dropped support for Python 2 a while ago, but didn't notice
this test needed updating because it was skipped.

Another test still needs a python 2 fixture to ensure that lockfiles
still pinned to python 2 throw the correct exception.

So copied the existing fixture, updated it to specify a python 3
version, and then regenerated the lockfile using:
```
PYENV_VERSION=3.10.7 pyenv exec poetry lock --no-update
```
Copy link

github-actions bot commented Mar 1, 2024

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 1, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
kind/bug Something isn't working as expected
Projects
None yet
Development

No branches or pull requests