mirror of
https://github.com/nvbn/thefuck.git
synced 2025-02-20 20:09:07 +00:00
#N/A: Add port_already_in_use
rule
This commit is contained in:
parent
a8c3c2d728
commit
56851e8d31
@ -210,6 +210,7 @@ using the matched rule and runs it. Rules enabled by default are as follows:
|
||||
* `no_such_file` – creates missing directories with `mv` and `cp` commands;
|
||||
* `open` – either prepends `http://` to address passed to `open` or create a new file or directory and passes it to `open`;
|
||||
* `pip_unknown_command` – fixes wrong `pip` commands, for example `pip instatl/pip install`;
|
||||
* `port_already_in_use` – kills process that bound port;
|
||||
* `python_command` – prepends `python` when you trying to run not executable/without `./` python script;
|
||||
* `python_execute` – appends missing `.py` when executing Python files;
|
||||
* `quotation_marks` – fixes uneven usage of `'` and `"` when containing args';
|
||||
|
101
tests/rules/test_port_already_in_use.py
Normal file
101
tests/rules/test_port_already_in_use.py
Normal file
@ -0,0 +1,101 @@
|
||||
from io import BytesIO
|
||||
|
||||
import pytest
|
||||
from thefuck.rules.port_already_in_use import match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
outputs = [
|
||||
'''
|
||||
|
||||
DE 70% 1/1 build modulesevents.js:141
|
||||
throw er; // Unhandled 'error' event
|
||||
^
|
||||
|
||||
Error: listen EADDRINUSE 127.0.0.1:8080
|
||||
at Object.exports._errnoException (util.js:873:11)
|
||||
at exports._exceptionWithHostPort (util.js:896:20)
|
||||
at Server._listen2 (net.js:1250:14)
|
||||
at listen (net.js:1286:10)
|
||||
at net.js:1395:9
|
||||
at GetAddrInfoReqWrap.asyncCallback [as callback] (dns.js:64:16)
|
||||
at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:83:10)
|
||||
|
||||
''',
|
||||
'''
|
||||
[6:40:01 AM] <START> Building Dependency Graph
|
||||
[6:40:01 AM] <START> Crawling File System
|
||||
ERROR Packager can't listen on port 8080
|
||||
Most likely another process is already using this port
|
||||
Run the following command to find out which process:
|
||||
|
||||
lsof -n -i4TCP:8080
|
||||
|
||||
You can either shut down the other process:
|
||||
|
||||
kill -9 <PID>
|
||||
|
||||
or run packager on different port.
|
||||
|
||||
''',
|
||||
'''
|
||||
Traceback (most recent call last):
|
||||
File "/usr/lib/python3.5/runpy.py", line 184, in _run_module_as_main
|
||||
"__main__", mod_spec)
|
||||
File "/usr/lib/python3.5/runpy.py", line 85, in _run_code
|
||||
exec(code, run_globals)
|
||||
File "/home/nvbn/exp/code_view/server/code_view/main.py", line 14, in <module>
|
||||
web.run_app(app)
|
||||
File "/home/nvbn/.virtualenvs/code_view/lib/python3.5/site-packages/aiohttp/web.py", line 310, in run_app
|
||||
backlog=backlog))
|
||||
File "/usr/lib/python3.5/asyncio/base_events.py", line 373, in run_until_complete
|
||||
return future.result()
|
||||
File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
|
||||
raise self._exception
|
||||
File "/usr/lib/python3.5/asyncio/tasks.py", line 240, in _step
|
||||
result = coro.send(None)
|
||||
File "/usr/lib/python3.5/asyncio/base_events.py", line 953, in create_server
|
||||
% (sa, err.strerror.lower()))
|
||||
OSError: [Errno 98] error while attempting to bind on address ('0.0.0.0', 8080): address already in use
|
||||
Task was destroyed but it is pending!
|
||||
task: <Task pending coro=<RedisProtocol._reader_coroutine() running at /home/nvbn/.virtualenvs/code_view/lib/python3.5/site-packages/asyncio_redis/protocol.py:921> wait_for=<Future pending cb=[Task._wakeup()]>>
|
||||
'''
|
||||
]
|
||||
|
||||
lsof_stdout = b'''COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
|
||||
node 18233 nvbn 16u IPv4 557134 0t0 TCP localhost:http-alt (LISTEN)
|
||||
'''
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def lsof(mocker):
|
||||
patch = mocker.patch('thefuck.rules.port_already_in_use.Popen')
|
||||
patch.return_value.stdout = BytesIO(lsof_stdout)
|
||||
return patch
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('no_memoize')
|
||||
@pytest.mark.parametrize(
|
||||
'command',
|
||||
[Command('./app', stdout=output) for output in outputs]
|
||||
+ [Command('./app', stderr=output) for output in outputs])
|
||||
def test_match(command):
|
||||
assert match(command)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('no_memoize')
|
||||
@pytest.mark.parametrize('command, lsof_output', [
|
||||
(Command('./app'), lsof_stdout),
|
||||
(Command('./app', stdout=outputs[1]), b''),
|
||||
(Command('./app', stderr=outputs[2]), b'')])
|
||||
def test_not_match(lsof, command, lsof_output):
|
||||
lsof.return_value.stdout = BytesIO(lsof_output)
|
||||
|
||||
assert not match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'command',
|
||||
[Command('./app', stdout=output) for output in outputs]
|
||||
+ [Command('./app', stderr=output) for output in outputs])
|
||||
def test_get_new_command(command):
|
||||
assert get_new_command(command) == 'kill 18233 && ./app'
|
39
thefuck/rules/port_already_in_use.py
Normal file
39
thefuck/rules/port_already_in_use.py
Normal file
@ -0,0 +1,39 @@
|
||||
import re
|
||||
from subprocess import Popen, PIPE
|
||||
from thefuck.utils import memoize
|
||||
from thefuck.shells import shell
|
||||
|
||||
patterns = [r"bind on address \('.*', (?P<port>\d+)\)",
|
||||
r'Unable to bind [^ ]*:(?P<port>\d+)',
|
||||
r"can't listen on port (?P<port>\d+)",
|
||||
r'listen EADDRINUSE [^ ]*:(?P<port>\d+)']
|
||||
|
||||
|
||||
@memoize
|
||||
def _get_pid_by_port(port):
|
||||
proc = Popen(['lsof', '-i', ':{}'.format(port)], stdout=PIPE)
|
||||
lines = proc.stdout.read().decode().split('\n')
|
||||
if len(lines) > 1:
|
||||
return lines[1].split()[1]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
@memoize
|
||||
def _get_used_port(command):
|
||||
for pattern in patterns:
|
||||
matched = (re.search(pattern, command.stderr)
|
||||
or re.search(pattern, command.stdout))
|
||||
if matched:
|
||||
return matched.group('port')
|
||||
|
||||
|
||||
def match(command):
|
||||
port = _get_used_port(command)
|
||||
return port and _get_pid_by_port(port)
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
port = _get_used_port(command)
|
||||
pid = _get_pid_by_port(port)
|
||||
return shell.and_(u'kill {}'.format(pid), command.script)
|
Loading…
x
Reference in New Issue
Block a user