1
0
mirror of https://github.com/nvbn/thefuck.git synced 2025-11-01 23:51:59 +00:00

Compare commits

...

185 Commits

Author SHA1 Message Date
nvbn
c6af8409d9 Bump to 3.8 2016-04-08 14:36:53 +03:00
Vladimir Iakovlev
95e7d00aec Merge pull request #492 from scorphus/overridden-aliases
Treat overridden aliases in a better way
2016-04-07 13:09:08 +03:00
Pablo Santiago Blum de Aguiar
cdccf1881e #253: Use a better name for that env var 2016-04-06 23:11:19 -03:00
Pablo Santiago Blum de Aguiar
db6053b301 #253: Update default overridden aliases with user's 2016-04-06 22:58:08 -03:00
nvbn
183b70c8b8 Merge branch 'scorphus-git-branch-exists' 2016-04-03 14:08:26 +03:00
nvbn
5e0cc8c703 #491: yield possible fixes in git_branch_exists rule 2016-04-03 14:07:39 +03:00
nvbn
1aa2ec1795 Merge branch 'git-branch-exists' of https://github.com/scorphus/thefuck into scorphus-git-branch-exists 2016-04-03 14:02:32 +03:00
Pablo Santiago Blum de Aguiar
0c98053f74 #N/A Add a new rule git_branch_exists 2016-04-03 00:09:15 -03:00
Vladimir Iakovlev
17b2fba48d Merge pull request #489 from scorphus/improve-readme
Improve readme
2016-04-02 01:44:51 +03:00
Pablo Santiago Blum de Aguiar
43886c38ff #N/A Add more fancy badges 2016-03-31 22:07:10 -03:00
Pablo Santiago Blum de Aguiar
9070748a86 #N/A Use reference links 2016-03-31 22:06:01 -03:00
Pablo Santiago Blum de Aguiar
61de6f4a51 #N/A Reformat parts of README.md 2016-03-31 15:53:32 -03:00
Vladimir Iakovlev
d102af41d9 #488 Add AppVeyor badge 2016-03-31 04:50:40 +03:00
Vladimir Iakovlev
b7002bb9f9 Merge pull request #488 from scorphus/app-veyor
App veyor
2016-03-31 04:44:44 +03:00
Pablo Santiago Blum de Aguiar
18b4f5df6a #486: Run tests on AppVeyor 2016-03-29 23:39:53 -03:00
Pablo Santiago Blum de Aguiar
28153db4a8 #486: Ignore a test on Windows 2016-03-29 23:39:53 -03:00
Pablo Santiago Blum de Aguiar
047a1a6072 #486: Use Path instead of PosixPath 2016-03-29 23:39:53 -03:00
Pablo Santiago Blum de Aguiar
69db5c70e6 #486: Fix path joining on Windows 2016-03-29 23:39:45 -03:00
nvbn
fa1edd4bae Bump to 3.7 2016-03-23 05:12:29 +02:00
Vladimir Iakovlev
333c4b2a3f Merge pull request #483 from shakaran/patch-1
Update install procedure for pip
2016-03-23 05:11:07 +02:00
Vladimir Iakovlev
b1f10642fa Merge pull request #487 from scorphus/stdout-encoding-none
#486: Use alternative encoding when sys.stdout.encoding is None
2016-03-23 05:09:36 +02:00
Pablo Santiago Blum de Aguiar
047efd5575 #486: Use alternative encoding when sys.stdout.encoding is None
Fix #486
2016-03-22 16:13:59 -03:00
Vladimir Iakovlev
f604756cb7 Merge pull request #485 from scorphus/484-stdin-pipe
#484: Use PIPE as stdin when Popening the script
2016-03-21 22:35:18 +02:00
Pablo Santiago Blum de Aguiar
a27115bff1 #484: Use PIPE as stdin when Popening the script
Fix #484
2016-03-21 16:59:35 -03:00
Ángel Guzmán Maeso
5d00b3bc25 Update install procedure for pip
Avoid the warning:

The directory '/home/someuser/.cache/pip/http' or its parent directory is not owned by the current user and the cache has been disabled. Please check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
2016-03-21 17:48:41 +01:00
Vladimir Iakovlev
0cf4f5e8b0 Merge pull request #481 from scorphus/fix-git-add
Fix `git_add` rule
2016-03-19 12:59:22 +02:00
Pablo Santiago Blum de Aguiar
41707b80c6 #N/A: Fix git_add rule 2016-03-18 22:46:38 -03:00
Vladimir Iakovlev
3a39deb485 Merge pull request #478 from MattKotsenas/feature/powershell-shell
Add Powershell as shell
2016-03-19 02:45:26 +02:00
Matt Kotsenas
d4bc8cebf1 Replace raise with return for Ctrl+C in Windows
- Replace the raise `const.CtrlC` with `return const.CtrlC` the match the
  unix implementation and prevent a stacktrace when cancelling a command
  on Windows
2016-03-16 16:37:59 -07:00
Matt Kotsenas
6daf687237 Add Powershell as a supported shell
- There may be additional functionality to implement, but I've been
  running this way for a month with no known issues
2016-03-16 16:06:09 -07:00
Matt Kotsenas
2207dd2668 Update _get_shell to work with Windows
- _get_shell assumed the parent process would always be the shell process, in Powershell the
  parent process is Python, with the grandparent being the shell
- Switched to walking the process tree so the same code path can be used in both places
2016-03-15 14:10:04 -07:00
Vladimir Iakovlev
d1ab08a797 Merge pull request #477 from scorphus/git-rm-recursive
#N/A: Add new `git_rm_recursive` rule
2016-03-15 04:30:31 +03:00
Pablo Santiago Blum de Aguiar
51e89a36ef #N/A: Add new git_rm_recursive rule 2016-03-14 18:59:32 -03:00
nvbn
39f7cc37eb Bump to 3.6 2016-03-13 17:46:14 +03:00
nvbn
251b69b5a0 #475: Try to use already used executable in no_command 2016-03-13 15:10:37 +03:00
nvbn
8b1f078e27 Bump to 3.5 2016-03-13 03:14:18 +03:00
Vladimir Iakovlev
a47e84fa6b Merge pull request #476 from scorphus/git-help-aliased
#N/A: Add new `git_help_aliased` rule
2016-03-13 01:24:57 +03:00
Pablo Santiago Blum de Aguiar
bcab700215 #N/A: Add new git_help_aliased rule 2016-03-12 18:51:47 -03:00
Vladimir Iakovlev
4fb99fd7a8 Merge pull request #474 from scorphus/alias-variables
#301: Set variables within the alias
2016-03-10 13:25:50 +03:00
Pablo Santiago Blum de Aguiar
bb5f6bb705 #301: Set variables within the alias
Fix #301
2016-03-09 21:58:18 -03:00
Vladimir Iakovlev
d92765d5df Merge pull request #473 from scorphus/gdbm-unavailable
#439 & #447: Remove cache if created with unavailable db
2016-03-10 02:52:33 +03:00
Pablo Santiago Blum de Aguiar
d8de5cfd20 #439 & #447: Remove cache if created with unavailable db
When switching between Python versions, the database package used to
create the cache might be unavailable and an ImportError is raised,
such as `ImportError: No module named gdbm`.
2016-03-08 14:38:16 -03:00
Vladimir Iakovlev
d73b14ce4b Merge pull request #472 from PLNech/master
Rules: git remote add instead of set-url if remote does not exist
2016-03-06 13:33:14 +03:00
Paul-Louis NECH
04b83cf7e8 Rules: git remote add instead of set-url if remote does not exist
Tests: Added test for git_remote_seturl_add

Rules: renamed new rule with a more appropriate name

README: added new rule

Style: Formatting

New rule: corrected test name

Developed tests
2016-03-04 22:35:55 +01:00
nvbn
019f0abf67 Bump to 3.4 2016-03-04 00:21:51 +03:00
nvbn
c8d3dcd1bf Merge branch 'lawrencebenson-master' 2016-03-04 00:20:43 +03:00
nvbn
4f5b382df4 #471: Use parametrized tests 2016-03-04 00:20:33 +03:00
lawrencebenson
77ad68b04b Fix encoding error in source file example 2016-03-03 10:43:33 +01:00
lawrencebenson
6b0311181d Fix encoding error for Python 2 2016-03-02 23:19:22 +01:00
lawrencebenson
48808f93ac Add no hard link support for ln 2016-03-02 17:57:53 +01:00
nvbn
7ce4307c87 #402: Don't invoke bash for getting aliases 2016-03-01 01:28:21 +03:00
nvbn
f7f0660114 #402: Don't invoke zsh for getting aliases 2016-03-01 01:21:51 +03:00
nvbn
46f2351907 #N/A: zsh history doesn't update on travis-ci without manual fc -r call 2016-02-22 18:47:15 +03:00
nvbn
e9de01fa41 #N/A: Move all consts to const 2016-02-22 18:40:28 +03:00
nvbn
9b260eb239 ⚠️ #442: Change history only on shell side 2016-02-22 18:31:28 +03:00
Vladimir Iakovlev
f20d4dbf85 Merge pull request #465 from mcarton/histfile2
Let bash handle bash’s history
2016-02-22 17:31:59 +03:00
mcarton
4af7dc2748 Let bash handle bash’s history 2016-02-22 14:53:53 +01:00
Vladimir Iakovlev
c8e9606c7d Merge pull request #462 from scorphus/fish-alias
#N/A Remove fucked up cmd from history regardless of status
2016-02-22 15:48:06 +03:00
Pablo Santiago Blum de Aguiar
d71b9c2e62 #N/A Remove fucked up cmd from history regardless of status
Most fucked up commands are erroneous, but that's not always the case.
2016-02-18 15:17:46 -02:00
Vladimir Iakovlev
60497b9d04 Merge pull request #461 from mcarton/ruby-gem
Add ruby gem error message to sudo patterns
2016-02-18 12:21:54 +03:00
mcarton
619af2638a Make sudo patterns lowercase 2016-02-16 22:27:01 +01:00
mcarton
a1f15bfe5f Add ruby gem error message to sudo patterns 2016-02-16 22:26:22 +01:00
Vladimir Iakovlev
ce959b2a8b Merge pull request #458 from scorphus/anydbm
#439 #447 Except anydbm instead of gdbm for PY2
2016-02-15 14:21:37 +03:00
Pablo Santiago Blum de Aguiar
3cc4940842 #439 #447 Except anydbm instead of gdbm for PY2
Fix #439 and close #447
2016-02-14 00:43:07 -02:00
nvbn
470c0ef699 #444: Check pip version in setup.py 2016-02-12 20:11:08 +03:00
nvbn
4f95b3365a #N/A: Add grep_arguments_order rule 2016-02-08 11:04:19 +03:00
nvbn
bbfd53d718 #N/A: Use shutil.which when possible 2016-02-06 16:42:42 +03:00
nvbn
28d078708b #N/A: Check that brew/apt available in related rules 2016-02-06 16:37:11 +03:00
nvbn
778d5f3e6e #N/A: Add npm_wrong_command rule 2016-02-06 15:18:44 +03:00
Vladimir Iakovlev
c5acee54ea Merge pull request #452 from scorphus/increase-coverage
Increase coverage
2016-02-01 17:52:08 +03:00
Pablo Santiago Blum de Aguiar
c3e9c1bfc1 #N/A Completely test rules.apt_get 2016-01-31 19:55:45 -02:00
Pablo Santiago Blum de Aguiar
6c25b33b9e #N/A Add parameter and skip a test for apt_get rule 2016-01-31 19:55:45 -02:00
Pablo Santiago Blum de Aguiar
052f415d94 #N/A Completely test rules.gulp_not_task 2016-01-31 19:55:44 -02:00
Pablo Santiago Blum de Aguiar
3438d6dde7 #N/A Completely test rules.git_checkout 2016-01-31 19:55:44 -02:00
Vladimir Iakovlev
af0fe66a9f Merge pull request #441 from nvbn/simplify-shells
Split `shells` module
2016-01-31 00:04:35 +03:00
nvbn
fe07fcaa62 #441: Remove shells methods wrappers 2016-01-29 13:09:40 +03:00
nvbn
b5dc7aab6d #441: Remove all logic from shells methods wrappers 2016-01-29 12:30:31 +03:00
nvbn
a2ec5aa3ff #441: Move function for getting current alias to utils 2016-01-29 12:22:31 +03:00
nvbn
0b2bda9e85 Merge branch 'master' into simplify-shells 2016-01-29 12:15:05 +03:00
nvbn
68c882cf1a Merge branch 'fix-fish-how-to' of https://github.com/scorphus/thefuck into scorphus-fix-fish-how-to 2016-01-29 12:07:27 +03:00
nvbn
abe8fb84c6 Merge branch 'master' of github.com:nvbn/thefuck 2016-01-29 12:04:07 +03:00
nvbn
86b17eb570 Fix travis builds 2016-01-29 12:03:47 +03:00
Pablo Santiago Blum de Aguiar
dcc0ce7ff3 #449 Fix Fish.how_to_configure()
Fix #449
2016-01-28 20:47:17 -02:00
Vladimir Iakovlev
fee874cddc Merge pull request #443 from aureooms/patch-1
new pattern in sudo rule
2016-01-24 16:45:17 +03:00
Aurélien Ooms
2ce1c6bf90 new pattern in sudo rule
`pacman-key --refresh-keys` outputs the following when not run as root

```
==> ERROR: pacman-key needs to be run as root for this operation.
```
2016-01-24 14:38:13 +01:00
nvbn
9b129fad08 #N/A: Add docsting in shells 2016-01-24 04:39:27 +03:00
nvbn
20adefbe7d Merge branch 'master' into simplify-shells 2016-01-23 23:49:26 +03:00
nvbn
52ef1fa47d #438: Replace [[ with [ in install script 2016-01-23 23:48:51 +03:00
nvbn
94f8652175 #N/A: Add tests for tcsh 2016-01-23 05:06:33 +03:00
nvbn
abe287a52b #N/A: Split shells module 2016-01-23 05:06:22 +03:00
nvbn
60e19a054a #N/A: Replace PY3 checks with PY2 checks 2016-01-22 17:02:08 +03:00
Vladimir Iakovlev
45add1e3d6 Merge pull request #437 from BuBuaBu/master
Exclude recursively tests packages
2016-01-22 16:56:29 +03:00
Vladimir Iakovlev
934870f650 Merge pull request #436 from scorphus/shelve-open-errors
Fix cache problem when going from Python 3 to 2
2016-01-22 16:56:08 +03:00
BuBuaBu
738cc0c9a8 Exclude recursively tests packages 2016-01-22 13:47:31 +01:00
Pablo Santiago Blum de Aguiar
cb6f964a30 Fix cache problem when going from Python 3 to 2 2016-01-21 23:57:17 -02:00
Vladimir Iakovlev
9c9a7343de Merge pull request #435 from scorphus/cache-fish-aliases
#353 Cache Fish aliases too
2016-01-21 18:40:11 +03:00
Pablo Santiago Blum de Aguiar
3803452980 #353 Cache Fish aliases too 2016-01-20 21:43:00 -02:00
Vladimir Iakovlev
a19833d0c7 Merge pull request #434 from scorphus/433-ioencoding
#433: Set env vars right in the aliases
2016-01-17 14:00:23 +03:00
Pablo Santiago Blum de Aguiar
084b907ac0 #433: Set env vars right in the aliases
Fix #433
2016-01-16 21:35:15 -02:00
nvbn
bd5cf38271 Bump to 3.3 2016-01-13 22:33:04 +03:00
Vladimir Iakovlev
3c3d17e0ea Merge pull request #432 from nvbn/422-not-alter-history
#422: Add `alter_history` settings option
2016-01-13 22:31:19 +03:00
nvbn
2f353498de #422: Add alter_history settings option 2016-01-13 22:22:51 +03:00
Vladimir Iakovlev
f0f49c1865 Merge pull request #430 from nvbn/429-apt-invalid-operation
#429: Add `apt_invalid_operation` rule
2016-01-13 22:12:35 +03:00
nvbn
20fff3142c #429: Fix tests with python 2 2016-01-13 22:08:24 +03:00
Vladimir Iakovlev
6e22b9ec6c Merge pull request #431 from nvbn/428-readonly-history
#428: Don't fail when history is readonly
2016-01-13 22:03:31 +03:00
nvbn
d53240b777 #428: Don't fail when history is readonly 2016-01-13 22:00:20 +03:00
nvbn
cab933e7e6 #429: Add apt_invalid_operation rule 2016-01-13 21:53:11 +03:00
Vladimir Iakovlev
8b05f6d46f Merge pull request #427 from makalaaneesh/master
#425 command had to be re escaped
2016-01-08 16:38:05 +03:00
Vladimir Iakovlev
ec64fbd5ea Merge pull request #426 from web-connect/patch-1
Fixing typos
2016-01-08 16:37:23 +03:00
makalaaneesh
4f9fb796c4 fixes #425. command had to be re escaped 2016-01-08 00:50:26 +05:30
Justin Turner
be744f20ba Fixing typos 2016-01-07 09:36:37 -06:00
Vladimir Iakovlev
1b12cd85e9 Merge pull request #423 from MattKotsenas/bugfix/cd_mkdir
Add Windows error message support to cd_mkdir rule
2016-01-06 03:34:03 +03:00
Matt Kotsenas
47df80f6b8 Add Windows error message support to cd_mkdir rule
Add the Windows error message 'the system cannot find the path specified'
to the list of recognized messages for cd_mkdir.
2016-01-05 13:55:59 -08:00
Vladimir Iakovlev
a0ef0efe46 Merge pull request #419 from mcarton/fix-unzip
Fix the `dirty_unzip` rule
2015-12-30 00:58:30 +03:00
Vladimir Iakovlev
25662ad737 Merge pull request #418 from makalaaneesh/master
sudo sh execute for && in commands - preventing double sudo
2015-12-30 00:57:50 +03:00
mcarton
42b344676e Fix dirty_unzip rule on non-zip files 2015-12-29 18:46:35 +01:00
mcarton
a3e1cb6718 Fix thefuck unzip, fix #416 2015-12-29 18:38:58 +01:00
makalaaneesh
f249098336 sudo sh execute for && in commands - preventing double sudo 2015-12-23 14:35:47 +05:30
nvbn
c3b1ba7637 #415: Prevent double sudo 2015-12-11 07:41:13 +08:00
nvbn
b65a9a0a4f #414: Initialize output before any colorama import 2015-12-04 18:34:52 +08:00
nvbn
29c1d1efcf #414: Move system-dependent utils in system module 2015-12-03 20:03:27 +08:00
nvbn
0560f4ba8e #414: Install and use win_unicode_console only on windows 2015-12-01 20:15:27 +08:00
Pavel Krymets
f9aa0e7c6b Fix windows unicode output issues 2015-11-30 16:24:31 -08:00
Pavel Krymets
b18a049886 Fix getch on windows 2015-11-30 12:33:28 -08:00
nvbn
9192b555b5 Merge branch 'master' of github.com:nvbn/thefuck 2015-11-26 03:42:16 +08:00
nvbn
d750d3d6d1 #412: Add _script_from_history for generic shell 2015-11-26 03:42:03 +08:00
Vladimir Iakovlev
3ad953001d Merge pull request #411 from scorphus/unicode
Support non-ascii content in Python 2
2015-11-25 20:41:20 +08:00
Pablo Santiago Blum de Aguiar
3b4b87d8ed #398: Test PYTHONIOENCODING=utf-8 in shell aliases 2015-11-25 02:34:33 -02:00
Pablo Santiago Blum de Aguiar
6c3d67763a #398: Add PYTHONIOENCODING=utf-8 to Fish Shell alias 2015-11-25 02:34:33 -02:00
Pablo Santiago Blum de Aguiar
959680d24d #N/A Set TF_ALIAS as an environment variable
For more info, check:

http://fishshell.com/docs/current/faq.html#faq-single-env
2015-11-25 02:34:33 -02:00
Pablo Santiago Blum de Aguiar
b0adc7f2ca #N/A Indent Fish alias with two spaces (default) 2015-11-25 02:34:33 -02:00
Pablo Santiago Blum de Aguiar
fc05364233 #398 & #408: Support non-ascii IO in Python 2 2015-11-25 02:34:19 -02:00
Pablo Santiago Blum de Aguiar
ad3db4ac67 #N/A Fix F812 list comprehension redefines cmd 2015-11-25 02:34:15 -02:00
Pablo Santiago Blum de Aguiar
4a7b335d7c #N/A Add ability to get Fish Shell history 2015-11-25 02:34:02 -02:00
Pablo Santiago Blum de Aguiar
465f6191b0 #N/A Cleanup and adjust syntax 2015-11-25 01:58:07 -02:00
Vladimir Iakovlev
b2836319ad Update README.md 2015-11-19 11:22:56 +08:00
Vladimir Iakovlev
b3e9b36bd1 Merge pull request #409 from nvbn/394-history-limit
#394 history limit
2015-11-19 11:17:21 +08:00
lovedboy
ae2949cfa2 python2.7 unicode error 2015-11-19 09:40:44 +08:00
nvbn
1bb04b41eb #398: Add PYTHONIOENCODING=utf-8 to shell aliases 2015-11-18 18:37:11 +08:00
Vladimir Iakovlev
acd0b3e024 Merge pull request #406 from mcarton/py2→3
Fix cache problem when going from Python 2 to 3
2015-11-18 18:32:24 +08:00
mcarton
7c5676491a Fix some more warnings from flake8 2015-11-15 18:08:59 +01:00
mcarton
8feb722ed0 Fix some pep8 warnings 2015-11-15 18:02:37 +01:00
mcarton
c3ea2fd0c7 Fix cache problem when going from Python 2 to 3 2015-11-15 16:55:07 +01:00
nvbn
b55464b2ea #403 Add sudo rule's pattern for dscl 2015-11-13 15:37:13 +08:00
nvbn
8ddb61ae89 #N/A Add python-gdbm to install script 2015-11-12 18:43:15 +08:00
Vladimir Iakovlev
fe91008a9c Merge pull request #400 from alessio/fix-memoize
Fix misinterpretation of the disabled flag
2015-11-06 02:19:07 +08:00
Alessio Treglia
7f777213c5 Fix misinterpretation of the disabled flag
The old implementation was misinterpretating the disabled flag and
effectively applying memoization even when explicitly disabled.
The 'or' operator is a short-circuit one; namely, it evaluates the
second argument if and only if the first is False. Therefore the
following conditions caused unexpected side effects:

- memoize.disabled = True, key not yet memoized

  Having disabled the memoize function wrapper, the client expects
  that no memoization happens. Instead the execution enters the
  if clause and store the value into the 'memo' dictionary

- memoize.disabled = True, key memoized

  Having disabled the memoize function wrapper, the client expects
  that no memoization happens and the function will be evaluated
  anyway, whether or not its return value had already been stored in
  the 'memo' dictionary by a previous call. On the contrary, the last
  statement of wrapper() access the value stored by the last function
  execution.

This commit attempts to improve the function readability too.
2015-11-04 22:44:50 +00:00
nvbn
89f868c115 Bump to 3.2 2015-11-03 17:38:07 +08:00
nvbn
81f6a25abc #398: Fix UnicodeDecodeError in logs 2015-11-01 14:42:48 +08:00
nvbn
cc9af78787 Merge branch 'master' of github.com:nvbn/thefuck 2015-11-01 13:17:15 +08:00
nvbn
1fc3f1b5bf #398: Fix UnicodeDecodeError in logs 2015-11-01 13:16:58 +08:00
Vladimir Iakovlev
45574d06c9 Merge pull request #397 from janek-warchol/use-force-with-lease
Use --force-with-lease instead of --force for git push
2015-10-31 02:47:51 +08:00
Jan Warchoł
dc23d67a42 Use --force-with-lease instead of --force for git push
--force flag can be very dangerous, because it unconditionally
overwrites remote branch - if someone pushed new commits to the remote
repo after you last fetched/pulled, and you do push --force, you will
overwrite his commits without even knowing that you did that.  Using
--force-with-lease is much safer because it only overwrites remote
branch when it points to the same commit that you think it points to.

Read more:
https://developer.atlassian.com/blog/2015/04/force-with-lease/
2015-10-30 16:17:56 +01:00
nvbn
2fea0d4c60 #394: Force history_limit to be int 2015-10-30 16:23:19 +08:00
nvbn
8c8abca8d5 #394: readlines isn't lazy 2015-10-29 22:51:30 +08:00
nvbn
bd6ee68c03 #394: Try simpler solution to limit lines count 2015-10-29 20:17:17 +08:00
nvbn
16533e85a7 Merge branch 'debug' of git://github.com/lovedboy/thefuck into lovedboy-debug 2015-10-29 20:00:04 +08:00
lovedboy
b3a19fe439 history limit from settings 2015-10-29 10:14:34 +08:00
nvbn
959b96cf6e #392: Show only debug message if script isn't splitable 2015-10-29 01:03:27 +08:00
nvbn
f20311fa89 #392: Little refactoring 2015-10-29 00:13:59 +08:00
nvbn
a4c391096a Merge branch 'fix-split' of https://github.com/mcarton/thefuck into mcarton-fix-split 2015-10-29 00:04:29 +08:00
mcarton
e71a3e0cdb Replace (almost) all instance of script.split 2015-10-28 16:43:24 +01:00
mcarton
2d995d464f Fix the cpp11 rule 2015-10-28 15:27:10 +01:00
mcarton
280751b36e Fix the unzip rules and filenames with spaces 2015-10-28 15:13:33 +01:00
mcarton
0a6a3db65d Fix the untar rules and filenames with spaces 2015-10-28 15:12:59 +01:00
mcarton
ecfc180280 Add shells.quote 2015-10-28 14:16:01 +01:00
mcarton
dae58211ba Parse command line with shlex
I put that in shells so that weird shells might try to parse it
differently.
2015-10-28 14:01:14 +01:00
Vladimir Iakovlev
5e9b2c56da Merge pull request #391 from mcarton/tox-3.5
#374 Test python 3.5 with tox
2015-10-28 20:52:45 +08:00
mcarton
192ab0bfb0 Test python 3.5 with tox 2015-10-28 13:32:37 +01:00
lovedboy
372e983459 add THEFUCK_HISTORY_LIMIT, my machine is so slow 2015-10-22 19:25:00 +08:00
nvbn
346cb99217 #385 Little refactoring 2015-10-21 18:13:22 +08:00
nvbn
bbfedb861f Merge branch 'xdg' of https://github.com/mcarton/thefuck into mcarton-xdg 2015-10-21 18:03:39 +08:00
nvbn
f5b0e96747 #382 Prevent partial execution of install.sh 2015-10-21 18:00:08 +08:00
Vladimir Iakovlev
12a33f56bc Merge pull request #389 from scorphus/fix-touch
Fix `rules.touch` tests
2015-10-21 17:50:09 +08:00
Pablo Santiago Blum de Aguiar
590fdba2aa Fix rules.touch tests
Move them to `rules` sub-directory and import `shells` instead of `and_`
which in turn triggers the `generic_shell` fixture fixing the tests on
Fish Shell.
2015-10-18 19:49:46 -02:00
Vladimir Iakovlev
f374142bf8 Merge pull request #384 from scorphus/fish-func
Improve the Fish Shell function making it faster
2015-10-19 01:50:53 +08:00
Vladimir Iakovlev
540ff7e16d Merge pull request #387 from scorphus/git-two-dashes
Add `git_two_dashes` rule
2015-10-19 01:50:04 +08:00
Vladimir Iakovlev
806dad18bf Merge pull request #386 from mcarton/CONTRIBUTING
Add a CONTRIBUTING file
2015-10-19 01:49:02 +08:00
Vladimir Iakovlev
8b416f269f Merge pull request #388 from scorphus/fix-brew-tests
Fix rules.brew_install tests on Mac
2015-10-19 01:48:53 +08:00
Pablo Santiago Blum de Aguiar
5e44fb22be Fix rules.brew_install tests on Mac 2015-10-17 18:46:07 -03:00
Pablo Santiago Blum de Aguiar
5389d0c106 Add git_two_dashes rule 2015-10-17 18:40:53 -03:00
mcarton
c970f190d2 Add a CONTRIBUTING file 2015-10-17 15:45:13 +02:00
mcarton
8f25c95f06 Use XDG_CACHE_HOME for cache 2015-10-16 17:33:52 +02:00
mcarton
4a48108c69 Follow the XDG Base Directory Specification 2015-10-16 16:52:03 +02:00
Pablo Santiago Blum de Aguiar
f5e8fe954e Improve the Fish Shell function making it faster
It works now with no temp file involved, which makes it a lot faster.
Also, `history --merge`, although only supported on Fish Shell 2.2+,
merges the corrected entry back into history. Neat!

Ref #89
2015-10-16 00:42:34 -03:00
137 changed files with 2347 additions and 1164 deletions

View File

@@ -17,8 +17,9 @@ addons:
- pandoc
- git
install:
- pip install coveralls
- pip install -r requirements.txt
- pip install -U pip
- pip install -U coveralls
- pip install -Ur requirements.txt
- python setup.py develop
- rm -rf build
script:

25
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,25 @@
# Report issues
If you have any issue with The Fuck, sorry about that, but we will do what we
can to fix that. Actually, maybe we already have, so first thing to do is to
update The Fuck and see if the bug is still there.
If it is (sorry again), check if the problem has not already been reported and
if not, just open an issue on [GitHub](https://github.com/nvbn/thefuck) with
the following basic information:
- the output of `thefuck --version` (something like `The Fuck 3.1 using
Python 3.5.0`);
- your shell and its version (`bash`, `zsh`, *Windows PowerShell*, etc.);
- your system (Debian 7, ArchLinux, Windows, etc.);
- how to reproduce the bug;
- the output of The Fuck with `THEFUCK_DEBUG=true` exported (typically execute
`export THEFUCK_DEBUG=true` in your shell before The Fuck);
- if the bug only appears with a specific application, the output of that
application and its version;
- anything else you think is relevant.
It's only with enough information that we can do something to fix the problem.
# Make a pull request
We gladly accept pull request on the [official
repository](https://github.com/nvbn/thefuck) for new rules, new features, bug
fixes, etc.

View File

@@ -1,10 +1,10 @@
# The Fuck [![Build Status](https://travis-ci.org/nvbn/thefuck.svg?branch=master)](https://travis-ci.org/nvbn/thefuck)
# The Fuck [![Version][version-badge]][version-link] [![Build Status][travis-badge]][travis-link] [![Windows Build Status][appveyor-badge]][appveyor-link] [![Coverage][coverage-badge]][coverage-link] [![MIT License][license-badge]](LICENSE.md)
Magnificent app which corrects your previous console command,
inspired by a [@liamosaur](https://twitter.com/liamosaur/)
[tweet](https://twitter.com/liamosaur/status/506975850596536320).
[![gif with examples](https://raw.githubusercontent.com/nvbn/thefuck/master/example.gif)](https://raw.githubusercontent.com/nvbn/thefuck/master/example.gif)
[![gif with examples][examples-link]][examples-link]
Few more examples:
@@ -52,7 +52,7 @@ Python 3.4.2 (default, Oct 8 2014, 13:08:17)
git: 'brnch' is not a git command. See 'git --help'.
Did you mean this?
branch
branch
➜ fuck
git branch [enter/↑/↓/ctrl+c]
@@ -97,7 +97,7 @@ Reading package lists... Done
## Installation [*experimental*]
On Ubuntu and OS X you can install `The Fuck` with installation script:
```bash
wget -O - https://raw.githubusercontent.com/nvbn/thefuck/master/install.sh | sh - && $0
```
@@ -107,7 +107,7 @@ wget -O - https://raw.githubusercontent.com/nvbn/thefuck/master/install.sh | sh
Install `The Fuck` with `pip`:
```bash
sudo pip install thefuck
sudo -H pip install thefuck
```
[Or using an OS package manager (OS X, Ubuntu, Arch).](https://github.com/nvbn/thefuck/wiki/Installation)
@@ -155,19 +155,25 @@ using the matched rule and runs it. Rules enabled by default are as follows:
* `dry` – fixes repetitions like `git git push`;
* `fix_alt_space` – replaces Alt+Space with Space character;
* `fix_file` – opens a file with an error in your `$EDITOR`;
* `git_add` – fixes *"Did you forget to 'git add'?"*;
* `git_add` – fixes *"pathspec 'foo' did not match any file(s) known to git."*;
* `git_branch_delete` – changes `git branch -d` to `git branch -D`;
* `git_branch_exists` – offers `git branch -d foo`, `git branch -D foo` or `git checkout foo` when creating a branch that already exists;
* `git_branch_list` – catches `git branch list` in place of `git branch` and removes created branch;
* `git_checkout` – fixes branch name or creates new branch;
* `git_diff_staged` – adds `--staged` to previous `git diff` with unexpected output;
* `git_fix_stash` – fixes `git stash` commands (misspelled subcommand and missing `save`);
* `git_help_aliased` &ndash; fixes `git help <alias>` commands replacing <alias> with the aliased command;
* `git_not_command` &ndash; fixes wrong git commands like `git brnch`;
* `git_pull` &ndash; sets upstream before executing previous `git pull`;
* `git_pull_clone` &ndash; clones instead of pulling when the repo does not exist;
* `git_push` &ndash; adds `--set-upstream origin $branch` to previous failed `git push`;
* `git_push_pull` &ndash; runs `git pull` when `push` was rejected;
* `git_rm_recursive` &ndash; adds `-r` when you try to `rm` a directory;
* `git_remote_seturl_add` &ndash; runs `git remote add` when `git remote set_url` on nonexistant remote;
* `git_stash` &ndash; stashes you local modifications before rebasing or switching branch;
* `go_run` &ndash; appends `.go` extension when compiling/running Go programs
* `git_two_dashes` &ndash; adds a missing dash to commands like `git commit -amend` or `git rebase -continue`;
* `go_run` &ndash; appends `.go` extension when compiling/running Go programs;
* `grep_arguments_order` &ndash; fixes grep arguments order for situations like `grep -lir . test`;
* `grep_recursive` &ndash; adds `-r` when you trying to `grep` directory;
* `gulp_not_task` &ndash; fixes misspelled `gulp` tasks;
* `has_exists_script` &ndash; prepends `./` when script/binary exists;
@@ -176,6 +182,7 @@ using the matched rule and runs it. Rules enabled by default are as follows:
* `java` &ndash; removes `.java` extension when running Java programs;
* `javac` &ndash; appends missing `.java` when compiling Java files;
* `lein_not_task` &ndash; fixes wrong `lein` tasks like `lein rpl`;
* `ln_no_hard_link` &ndash; catches hard link creation on directories, suggest symbolic link;
* `ls_lah` &ndash; adds `-lah` to `ls`;
* `man` &ndash; changes manual section;
* `man_no_space` &ndash; fixes man commands without spaces, for example `mandiff`;
@@ -183,6 +190,7 @@ using the matched rule and runs it. Rules enabled by default are as follows:
* `mkdir_p` &ndash; adds `-p` when you trying to create directory without parent;
* `mvn_no_command` &ndash; adds `clean package` to `mvn`;
* `mvn_unknown_lifecycle_phase` &ndash; fixes misspelled lifecycle phases with `mvn`;
* `npm_wrong_command` &ndash; fixes wrong npm commands like `npm urgrade`;
* `no_command` &ndash; fixes wrong console commands, for example `vom/vim`;
* `no_such_file` &ndash; creates missing directories with `mv` and `cp` commands;
* `open` &ndash; prepends `http` to address passed to `open`;
@@ -210,6 +218,7 @@ Enabled by default only on specific platforms:
* `apt_get` &ndash; installs app from apt if it not installed (requires `python-commandnotfound` / `python3-commandnotfound`);
* `apt_get_search` &ndash; changes trying to search using `apt-get` with searching using `apt-cache`;
* `apt_invalid_operation` &ndash; fixes invalid `apt` and `apt-get` calls, like `apt-get isntall vim`;
* `brew_install` &ndash; fixes formula name for `brew install`;
* `brew_unknown_command` &ndash; fixes wrong brew commands, for example `brew docto/brew doctor`;
* `brew_upgrade` &ndash; appends `--all` to `brew upgrade` as per Homebrew's new behaviour;
@@ -218,13 +227,13 @@ Enabled by default only on specific platforms:
Bundled, but not enabled by default:
* `git_push_force` &ndash; adds `--force` to a `git push` (may conflict with `git_push_pull`);
* `git_push_force` &ndash; adds `--force-with-lease` to a `git push` (may conflict with `git_push_pull`);
* `rm_root` &ndash; adds `--no-preserve-root` to `rm -rf /` command.
## Creating your own rules
For adding your own rule you should create `your-rule-name.py`
in `~/.thefuck/rules`. The rule should contain two functions:
in `~/.config/thefuck/rules`. The rule should contain two functions:
```python
match(command: Command) -> bool
@@ -241,7 +250,7 @@ and optional `enabled_by_default`, `requires_output` and `priority` variables.
`Command` has three attributes: `script`, `stdout` and `stderr`.
*Rules api changed in 3.0:* For accessing settings in rule you need to import it with `from thefuck.conf import settings`.
`settings` is a special object filled with `~/.thefuck/settings.py` and values from env ([see more below](#settings)).
`settings` is a special object filled with `~/.config/thefuck/settings.py` and values from env ([see more below](#settings)).
Simple example of the rule for running script with `sudo`:
@@ -271,7 +280,7 @@ requires_output = True
## Settings
The Fuck has a few settings parameters which can be changed in `~/.thefuck/settings.py`:
The Fuck has a few settings parameters which can be changed in `$XDG_CONFIG_HOME/thefuck/settings.py` (`$XDG_CONFIG_HOME` defaults to `~/.config`):
* `rules` &ndash; list of enabled rules, by default `thefuck.conf.DEFAULT_RULES`;
* `exclude_rules` &ndash; list of disabled rules, by default `[]`;
@@ -279,7 +288,9 @@ The Fuck has a few settings parameters which can be changed in `~/.thefuck/setti
* `wait_command` &ndash; max amount of time in seconds for getting previous command output;
* `no_colors` &ndash; disable colored output;
* `priority` &ndash; dict with rules priorities, rule with lower `priority` will be matched first;
* `debug` &ndash; enables debug output, by default `False`.
* `debug` &ndash; enables debug output, by default `False`;
* `history_limit` &ndash; numeric value of how many history commands will be scanned, like `2000`;
* `alter_history` &ndash; push fixed command to history, by default `True`.
Example of `settings.py`:
@@ -296,13 +307,15 @@ debug = False
Or via environment variables:
* `THEFUCK_RULES` &ndash; list of enabled rules, like `DEFAULT_RULES:rm_root` or `sudo:no_command`;
* `THEFUCK_EXCLUDE_RULES` &ndash; list of disabled rules, like `git_pull:git_push`;
* `THEFUCK_EXCLUDE_RULES` &ndash; list of disabled rules, like `git_pull:git_push`;
* `THEFUCK_REQUIRE_CONFIRMATION` &ndash; require confirmation before running new command, `true/false`;
* `THEFUCK_WAIT_COMMAND` &ndash; max amount of time in seconds for getting previous command output;
* `THEFUCK_NO_COLORS` &ndash; disable colored output, `true/false`;
* `THEFUCK_PRIORITY` &ndash; priority of the rules, like `no_command=9999:apt_get=100`,
rule with lower `priority` will be matched first;
* `THEFUCK_DEBUG` &ndash; enables debug output, `true/false`.
* `THEFUCK_DEBUG` &ndash; enables debug output, `true/false`;
* `THEFUCK_HISTORY_LIMIT` &ndash; how many history commands will be scanned, like `2000`;
* `THEFUCK_ALTER_HISTORY` &ndash; push fixed command to history `true/false`.
For example:
@@ -313,6 +326,7 @@ export THEFUCK_REQUIRE_CONFIRMATION='true'
export THEFUCK_WAIT_COMMAND=10
export THEFUCK_NO_COLORS='false'
export THEFUCK_PRIORITY='no_command=9999:apt_get=100'
export THEFUCK_HISTORY_LIMIT='2000'
```
## Developing
@@ -345,3 +359,15 @@ sudo apt-get install pandoc
## License MIT
Project License can be found [here](LICENSE.md).
[version-badge]: https://img.shields.io/pypi/v/thefuck.svg?label=version
[version-link]: https://pypi.python.org/pypi/thefuck/
[travis-badge]: https://img.shields.io/travis/nvbn/thefuck.svg
[travis-link]: https://travis-ci.org/nvbn/thefuck
[appveyor-badge]: https://img.shields.io/appveyor/ci/nvbn/thefuck.svg?label=windows%20build
[appveyor-link]: https://ci.appveyor.com/project/nvbn/thefuck
[coverage-badge]: https://img.shields.io/coveralls/nvbn/thefuck.svg
[coverage-link]: https://coveralls.io/github/nvbn/thefuck
[license-badge]: https://img.shields.io/badge/license-MIT-007EC7.svg
[examples-link]: https://raw.githubusercontent.com/nvbn/thefuck/master/example.gif

22
appveyor.yml Normal file
View File

@@ -0,0 +1,22 @@
build: false
environment:
matrix:
- PYTHON: "C:/Python27"
- PYTHON: "C:/Python33"
- PYTHON: "C:/Python34"
- PYTHON: "C:/Python35"
init:
- "ECHO %PYTHON%"
- ps: "ls C:/Python*"
install:
- ps: (new-object net.webclient).DownloadFile('https://bootstrap.pypa.io/get-pip.py', 'C:/get-pip.py')
- "%PYTHON%/python.exe C:/get-pip.py"
- "%PYTHON%/Scripts/pip.exe install -U setuptools"
- "%PYTHON%/python.exe setup.py develop"
- "%PYTHON%/Scripts/pip.exe install -U -r requirements.txt"
test_script:
- "%PYTHON%/Scripts/py.test.exe -sv"

View File

@@ -8,50 +8,54 @@ installed () {
hash $1 2>/dev/null
}
# Install os dependencies:
if installed apt-get; then
# Debian/ubuntu:
sudo apt-get update -yy
sudo apt-get install -yy python-pip python-dev command-not-found
install_thefuck () {
# Install OS dependencies:
if installed apt-get; then
# Debian/Ubuntu:
sudo apt-get update -yy
sudo apt-get install -yy python-pip python-dev command-not-found python-gdbm
if [[ -n $(apt-cache search python-commandnotfound) ]]; then
# In case of different python versions:
sudo apt-get install -yy python-commandnotfound
fi
else
if installed brew; then
# OS X:
brew update
brew install python
if [ -n "$(apt-cache search python-commandnotfound)" ]; then
# In case of different python versions:
sudo apt-get install -yy python-commandnotfound
fi
else
# Genreic way:
wget https://bootstrap.pypa.io/get-pip.py
sudo python get-pip.py
rm get-pip.py
if installed brew; then
# OS X:
brew update
brew install python
else
# Generic way:
wget https://bootstrap.pypa.io/get-pip.py
sudo python get-pip.py
rm get-pip.py
fi
fi
fi
# thefuck requires fresh versions of setuptools and pip:
sudo pip install -U pip setuptools
sudo pip install -U thefuck
# thefuck requires fresh versions of setuptools and pip:
sudo pip install -U pip setuptools
sudo pip install -U thefuck
# Setup aliases:
if should_add_alias ~/.bashrc; then
echo 'eval $(thefuck --alias)' >> ~/.bashrc
fi
# Setup aliases:
if should_add_alias ~/.bashrc; then
echo 'eval $(thefuck --alias)' >> ~/.bashrc
fi
if should_add_alias ~/.bash_profile; then
echo 'eval $(thefuck --alias)' >> ~/.bash_profile
fi
if should_add_alias ~/.bash_profile; then
echo 'eval $(thefuck --alias)' >> ~/.bash_profile
fi
if should_add_alias ~/.zshrc; then
echo 'eval $(thefuck --alias)' >> ~/.zshrc
fi
if should_add_alias ~/.zshrc; then
echo 'eval $(thefuck --alias)' >> ~/.zshrc
fi
if should_add_alias ~/.config/fish/config.fish; then
thefuck --alias >> ~/.config/fish/config.fish
fi
if should_add_alias ~/.config/fish/config.fish; then
thefuck --alias >> ~/.config/fish/config.fish
fi
if should_add_alias ~/.tcshrc; then
echo 'eval `thefuck --alias`' >> ~/.tcshrc
fi
if should_add_alias ~/.tcshrc; then
echo 'eval `thefuck --alias`' >> ~/.tcshrc
fi
}
install_thefuck

View File

@@ -1,3 +1,4 @@
pip
pytest
mock
pytest-mock

View File

@@ -1,8 +1,14 @@
#!/usr/bin/env python
from setuptools import setup, find_packages
import pip
import sys
import os
if int(pip.__version__.split('.')[0]) < 6:
print('pip older than 6.0 not supported, please upgrade pip with:\n\n'
' pip install -U pip')
sys.exit(-1)
if os.environ.get('CONVERT_README'):
import pypandoc
@@ -20,10 +26,11 @@ elif (3, 0) < version < (3, 3):
' ({}.{} detected).'.format(*version))
sys.exit(-1)
VERSION = '3.1'
VERSION = '3.8'
install_requires = ['psutil', 'colorama', 'six', 'decorator']
extras_require = {':python_version<"3.4"': ['pathlib']}
extras_require = {':python_version<"3.4"': ['pathlib'],
":sys_platform=='win32'": ['win_unicode_console']}
setup(name='thefuck',
version=VERSION,
@@ -34,7 +41,7 @@ setup(name='thefuck',
url='https://github.com/nvbn/thefuck',
license='MIT',
packages=find_packages(exclude=['ez_setup', 'examples',
'tests', 'release']),
'tests', 'tests.*', 'release']),
include_package_data=True,
zip_safe=False,
install_requires=install_requires,

View File

@@ -1,6 +1,9 @@
from pathlib import Path
import pytest
from thefuck import conf
from thefuck import shells
from thefuck import conf, const
shells.shell = shells.Generic()
def pytest_addoption(parser):
@@ -19,7 +22,7 @@ def no_memoize(monkeypatch):
def settings(request):
def _reset_settings():
conf.settings.clear()
conf.settings.update(conf.DEFAULT_SETTINGS)
conf.settings.update(const.DEFAULT_SETTINGS)
request.addfinalizer(_reset_settings)
conf.settings.user_dir = Path('~/.thefuck')
@@ -46,3 +49,14 @@ def functional(request):
@pytest.fixture
def source_root():
return Path(__file__).parent.parent.resolve()
@pytest.fixture
def set_shell(monkeypatch, request):
def _set(cls):
shell = cls()
monkeypatch.setattr('thefuck.shells.shell', shell)
request.addfinalizer()
return shell
return _set

View File

@@ -49,7 +49,7 @@ def test_select_command_with_arrows(proc, TIMEOUT):
@pytest.mark.functional
@pytest.mark.once_without_docker
@pytest.mark.skip_without_docker
def test_refuse_with_confirmation(proc, TIMEOUT):
refuse_with_confirmation(proc, TIMEOUT)
history_not_changed(proc, TIMEOUT)

View File

@@ -1,6 +0,0 @@
import pytest
@pytest.fixture(autouse=True)
def generic_shell(monkeypatch):
monkeypatch.setattr('thefuck.shells.and_', lambda *x: ' && '.join(x))

View File

@@ -29,12 +29,27 @@ def test_match_mocked(cmdnf_mock, command, return_value):
assert get_packages.called
# python-commandnotfound is available in ubuntu 14.04+
@pytest.mark.skipif(not getattr(apt_get, 'enabled_by_default', True),
reason='Skip if python-commandnotfound is not available')
@pytest.mark.parametrize('command', [
Command(script='a_bad_cmd', stderr='a_bad_cmd: command not found'),
Command(script='vim', stderr=''), Command()])
def test_not_match(command):
assert not match(command)
@pytest.mark.parametrize('command, return_value', [
(Command(script='a_bad_cmd', stderr='a_bad_cmd: command not found'), []),
(Command(script='vim', stderr=''), []), (Command(), [])])
@patch('thefuck.rules.apt_get.CommandNotFound', create=True)
@patch.multiple(apt_get, create=True, apt_get='apt_get')
def test_not_match_mocked(cmdnf_mock, command, return_value):
get_packages = Mock(return_value=return_value)
cmdnf_mock.CommandNotFound.return_value = Mock(getPackages=get_packages)
assert not match(command)
# python-commandnotfound is available in ubuntu 14.04+
@pytest.mark.skipif(not getattr(apt_get, 'enabled_by_default', True),
reason='Skip if python-commandnotfound is not available')

View File

@@ -0,0 +1,122 @@
from io import BytesIO
import pytest
from tests.utils import Command
from thefuck.rules.apt_invalid_operation import match, get_new_command, \
_get_operations
invalid_operation = 'E: Invalid operation {}'.format
apt_help = b'''apt 1.0.10.2ubuntu1 for amd64 compiled on Oct 5 2015 15:55:05
Usage: apt [options] command
CLI for apt.
Basic commands:
list - list packages based on package names
search - search in package descriptions
show - show package details
update - update list of available packages
install - install packages
remove - remove packages
upgrade - upgrade the system by installing/upgrading packages
full-upgrade - upgrade the system by removing/installing/upgrading packages
edit-sources - edit the source information file
'''
apt_operations = ['list', 'search', 'show', 'update', 'install', 'remove',
'upgrade', 'full-upgrade', 'edit-sources']
apt_get_help = b'''apt 1.0.10.2ubuntu1 for amd64 compiled on Oct 5 2015 15:55:05
Usage: apt-get [options] command
apt-get [options] install|remove pkg1 [pkg2 ...]
apt-get [options] source pkg1 [pkg2 ...]
apt-get is a simple command line interface for downloading and
installing packages. The most frequently used commands are update
and install.
Commands:
update - Retrieve new lists of packages
upgrade - Perform an upgrade
install - Install new packages (pkg is libc6 not libc6.deb)
remove - Remove packages
autoremove - Remove automatically all unused packages
purge - Remove packages and config files
source - Download source archives
build-dep - Configure build-dependencies for source packages
dist-upgrade - Distribution upgrade, see apt-get(8)
dselect-upgrade - Follow dselect selections
clean - Erase downloaded archive files
autoclean - Erase old downloaded archive files
check - Verify that there are no broken dependencies
changelog - Download and display the changelog for the given package
download - Download the binary package into the current directory
Options:
-h This help text.
-q Loggable output - no progress indicator
-qq No output except for errors
-d Download only - do NOT install or unpack archives
-s No-act. Perform ordering simulation
-y Assume Yes to all queries and do not prompt
-f Attempt to correct a system with broken dependencies in place
-m Attempt to continue if archives are unlocatable
-u Show a list of upgraded packages as well
-b Build the source package after fetching it
-V Show verbose version numbers
-c=? Read this configuration file
-o=? Set an arbitrary configuration option, eg -o dir::cache=/tmp
See the apt-get(8), sources.list(5) and apt.conf(5) manual
pages for more information and options.
This APT has Super Cow Powers.
'''
apt_get_operations = ['update', 'upgrade', 'install', 'remove', 'autoremove',
'purge', 'source', 'build-dep', 'dist-upgrade',
'dselect-upgrade', 'clean', 'autoclean', 'check',
'changelog', 'download']
@pytest.mark.parametrize('script, stderr', [
('apt', invalid_operation('saerch')),
('apt-get', invalid_operation('isntall')),
('apt-cache', invalid_operation('rumove'))])
def test_match(script, stderr):
assert match(Command(script, stderr=stderr))
@pytest.mark.parametrize('script, stderr', [
('vim', invalid_operation('vim')),
('apt-get', "")])
def test_not_match(script, stderr):
assert not match(Command(script, stderr=stderr))
@pytest.fixture
def set_help(mocker):
mock = mocker.patch('subprocess.Popen')
def _set_text(text):
mock.return_value.stdout = BytesIO(text)
return _set_text
@pytest.mark.parametrize('app, help_text, operations', [
('apt', apt_help, apt_operations),
('apt-get', apt_get_help, apt_get_operations)
])
def test_get_operations(set_help, app, help_text, operations):
set_help(help_text)
assert _get_operations(app) == operations
@pytest.mark.parametrize('script, stderr, help_text, result', [
('apt-get isntall vim', invalid_operation('isntall'),
apt_get_help, 'apt-get install vim'),
('apt saerch vim', invalid_operation('saerch'),
apt_help, 'apt search vim'),
])
def test_get_new_command(set_help, stderr, script, help_text, result):
set_help(help_text)
assert get_new_command(Command(script, stderr=stderr))[0] == result

View File

@@ -31,8 +31,7 @@ def test_match(brew_no_available_formula, brew_already_installed,
stderr=brew_no_available_formula))
assert not match(Command('brew install git',
stderr=brew_already_installed))
assert not match(Command('brew install', stderr=brew_install_no_argument),
None)
assert not match(Command('brew install', stderr=brew_install_no_argument))
@pytest.mark.skipif(_is_not_okay_to_test(),
@@ -43,5 +42,5 @@ def test_get_new_command(brew_no_available_formula):
== 'brew install elasticsearch'
assert get_new_command(Command('brew install aa',
stderr=brew_no_available_formula),
None) != 'brew install aha'
stderr=brew_no_available_formula))\
!= 'brew install aha'

View File

@@ -1,7 +1,8 @@
import os
import pytest
import tarfile
from thefuck.rules.dirty_untar import match, get_new_command, side_effect
from thefuck.rules.dirty_untar import match, get_new_command, side_effect, \
tar_extensions
from tests.utils import Command
@@ -32,34 +33,41 @@ def tar_error(tmpdir):
return fixture
parametrize_filename = pytest.mark.parametrize('filename', [
'foo.tar',
'foo.tar.gz',
'foo.tgz'])
parametrize_extensions = pytest.mark.parametrize('ext', tar_extensions)
# (filename as typed by the user, unquoted filename, quoted filename as per shells.quote)
parametrize_filename = pytest.mark.parametrize('filename, unquoted, quoted', [
('foo{}', 'foo{}', 'foo{}'),
('foo\ bar{}', 'foo bar{}', "'foo bar{}'"),
('"foo bar{}"', 'foo bar{}', "'foo bar{}'")])
parametrize_script = pytest.mark.parametrize('script, fixed', [
('tar xvf {}', 'mkdir -p foo && tar xvf {} -C foo'),
('tar -xvf {}', 'mkdir -p foo && tar -xvf {} -C foo'),
('tar --extract -f {}', 'mkdir -p foo && tar --extract -f {} -C foo')])
('tar xvf {}', 'mkdir -p {dir} && tar xvf {filename} -C {dir}'),
('tar -xvf {}', 'mkdir -p {dir} && tar -xvf {filename} -C {dir}'),
('tar --extract -f {}', 'mkdir -p {dir} && tar --extract -f {filename} -C {dir}')])
@parametrize_extensions
@parametrize_filename
@parametrize_script
def test_match(tar_error, filename, script, fixed):
tar_error(filename)
assert match(Command(script=script.format(filename)))
def test_match(ext, tar_error, filename, unquoted, quoted, script, fixed):
tar_error(unquoted.format(ext))
assert match(Command(script=script.format(filename.format(ext))))
@parametrize_extensions
@parametrize_filename
@parametrize_script
def test_side_effect(tar_error, filename, script, fixed):
tar_error(filename)
side_effect(Command(script=script.format(filename)), None)
assert set(os.listdir('.')) == {filename, 'd'}
def test_side_effect(ext, tar_error, filename, unquoted, quoted, script, fixed):
tar_error(unquoted.format(ext))
side_effect(Command(script=script.format(filename.format(ext))), None)
assert set(os.listdir('.')) == {unquoted.format(ext), 'd'}
@parametrize_extensions
@parametrize_filename
@parametrize_script
def test_get_new_command(tar_error, filename, script, fixed):
tar_error(filename)
assert get_new_command(Command(script=script.format(filename))) == fixed.format(filename)
def test_get_new_command(ext, tar_error, filename, unquoted, quoted, script, fixed):
tar_error(unquoted.format(ext))
assert (get_new_command(Command(script=script.format(filename.format(ext))))
== fixed.format(dir=quoted.format(''), filename=filename.format(ext)))

View File

@@ -1,48 +1,72 @@
# -*- coding: utf-8 -*-
import os
import pytest
import zipfile
from thefuck.rules.dirty_unzip import match, get_new_command, side_effect
from tests.utils import Command
from unicodedata import normalize
@pytest.fixture
def zip_error(tmpdir):
path = os.path.join(str(tmpdir), 'foo.zip')
def zip_error_inner(filename):
path = os.path.join(str(tmpdir), filename)
def reset(path):
with zipfile.ZipFile(path, 'w') as archive:
archive.writestr('a', '1')
archive.writestr('b', '2')
archive.writestr('c', '3')
def reset(path):
with zipfile.ZipFile(path, 'w') as archive:
archive.writestr('a', '1')
archive.writestr('b', '2')
archive.writestr('c', '3')
archive.writestr('d/e', '4')
archive.writestr('d/e', '4')
archive.extractall()
archive.extractall()
os.chdir(str(tmpdir))
reset(path)
os.chdir(str(tmpdir))
reset(path)
assert set(os.listdir('.')) == {'foo.zip', 'a', 'b', 'c', 'd'}
assert set(os.listdir('./d')) == {'e'}
dir_list = os.listdir(u'.')
if filename not in dir_list:
filename = normalize('NFD', filename)
assert set(dir_list) == {filename, 'a', 'b', 'c', 'd'}
assert set(os.listdir('./d')) == {'e'}
return zip_error_inner
@pytest.mark.parametrize('script', [
'unzip foo',
'unzip foo.zip'])
def test_match(zip_error, script):
@pytest.mark.parametrize('script,filename', [
(u'unzip café', u'café.zip'),
(u'unzip café.zip', u'café.zip'),
(u'unzip foo', u'foo.zip'),
(u'unzip foo.zip', u'foo.zip')])
def test_match(zip_error, script, filename):
zip_error(filename)
assert match(Command(script=script))
@pytest.mark.parametrize('script', [
'unzip foo',
'unzip foo.zip'])
def test_side_effect(zip_error, script):
@pytest.mark.parametrize('script,filename', [
(u'unzip café', u'café.zip'),
(u'unzip café.zip', u'café.zip'),
(u'unzip foo', u'foo.zip'),
(u'unzip foo.zip', u'foo.zip')])
def test_side_effect(zip_error, script, filename):
zip_error(filename)
side_effect(Command(script=script), None)
assert set(os.listdir('.')) == {'foo.zip', 'd'}
dir_list = os.listdir(u'.')
if filename not in set(dir_list):
filename = normalize('NFD', filename)
assert set(dir_list) == {filename, 'd'}
@pytest.mark.parametrize('script,fixed', [
('unzip foo', 'unzip foo -d foo'),
('unzip foo.zip', 'unzip foo.zip -d foo')])
def test_get_new_command(zip_error, script, fixed):
@pytest.mark.parametrize('script,fixed,filename', [
(u'unzip café', u"unzip café -d 'café'", u'café.zip'),
(u'unzip foo', u'unzip foo -d foo', u'foo.zip'),
(u"unzip foo\\ bar.zip", u"unzip foo\\ bar.zip -d 'foo bar'", u'foo.zip'),
(u"unzip 'foo bar.zip'", u"unzip 'foo bar.zip' -d 'foo bar'", u'foo.zip'),
(u'unzip foo.zip', u'unzip foo.zip -d foo', u'foo.zip')])
def test_get_new_command(zip_error, script, fixed, filename):
zip_error(filename)
assert get_new_command(Command(script=script)) == fixed

View File

@@ -1,3 +1,5 @@
# -*- coding: utf-8 -*-
import pytest
import os
from thefuck.rules.fix_file import match, get_new_command
@@ -87,6 +89,20 @@ Traceback (most recent call last):
TypeError: first argument must be string or compiled pattern
"""),
(u'python café.py', u'café.py', 8, None, '',
u"""
Traceback (most recent call last):
File "café.py", line 8, in <module>
match("foo")
File "café.py", line 5, in match
m = re.search(None, command)
File "/usr/lib/python3.4/re.py", line 170, in search
return _compile(pattern, flags).search(string)
File "/usr/lib/python3.4/re.py", line 293, in _compile
raise TypeError("first argument must be string or compiled pattern")
TypeError: first argument must be string or compiled pattern
"""),
('ruby a.rb', 'a.rb', 3, None, '',
"""
a.rb:3: syntax error, unexpected keyword_end
@@ -227,7 +243,7 @@ def test_get_new_command_with_settings(mocker, monkeypatch, test, settings):
if test[3]:
assert (get_new_command(cmd) ==
'dummy_editor {} +{}:{} && {}'.format(test[1], test[2], test[3], test[0]))
u'dummy_editor {} +{}:{} && {}'.format(test[1], test[2], test[3], test[0]))
else:
assert (get_new_command(cmd) ==
'dummy_editor {} +{} && {}'.format(test[1], test[2], test[0]))
u'dummy_editor {} +{} && {}'.format(test[1], test[2], test[0]))

View File

@@ -4,36 +4,28 @@ from tests.utils import Command
@pytest.fixture
def did_not_match(target, did_you_forget=True):
error = ("error: pathspec '{}' did not match any "
"file(s) known to git.".format(target))
if did_you_forget:
error = ("{}\nDid you forget to 'git add'?'".format(error))
return error
def stderr(target):
return ("error: pathspec '{}' did not match any "
'file(s) known to git.'.format(target))
@pytest.mark.parametrize('command', [
Command(script='git submodule update unknown',
stderr=did_not_match('unknown')),
Command(script='git commit unknown',
stderr=did_not_match('unknown'))]) # Older versions of Git
def test_match(command):
assert match(command)
@pytest.mark.parametrize('script, target', [
('git submodule update unknown', 'unknown'),
('git commit unknown', 'unknown')])
def test_match(stderr, script, target):
assert match(Command(script=script, stderr=stderr))
@pytest.mark.parametrize('command', [
Command(script='git submodule update known', stderr=('')),
Command(script='git commit known', stderr=('')),
Command(script='git commit unknown', # Newer versions of Git
stderr=did_not_match('unknown', False))])
def test_not_match(command):
assert not match(command)
@pytest.mark.parametrize('script', [
'git submodule update known', 'git commit known'])
def test_not_match(script):
assert not match(Command(script=script, stderr=''))
@pytest.mark.parametrize('command, new_command', [
(Command('git submodule update unknown', stderr=did_not_match('unknown')),
@pytest.mark.parametrize('script, target, new_command', [
('git submodule update unknown', 'unknown',
'git add -- unknown && git submodule update unknown'),
(Command('git commit unknown', stderr=did_not_match('unknown')), # Old Git
('git commit unknown', 'unknown',
'git add -- unknown && git commit unknown')])
def test_get_new_command(command, new_command):
assert get_new_command(command) == new_command
def test_get_new_command(stderr, script, target, new_command):
assert get_new_command(Command(script=script, stderr=stderr)) == new_command

View File

@@ -0,0 +1,33 @@
import pytest
from thefuck.rules.git_branch_exists import match, get_new_command
from tests.utils import Command
@pytest.fixture
def stderr(branch_name):
return "fatal: A branch named '{}' already exists.".format(branch_name)
@pytest.fixture
def new_command(branch_name):
return [cmd.format(branch_name) for cmd in [
'git branch -d {0} && git branch {0}',
'git branch -D {0} && git branch {0}', 'git checkout {0}']]
@pytest.mark.parametrize('script, branch_name', [
('git branch foo', 'foo'),
('git branch bar', 'bar')])
def test_match(stderr, script, branch_name):
assert match(Command(script=script, stderr=stderr))
@pytest.mark.parametrize('script', ['git branch foo', 'git branch bar'])
def test_not_match(script):
assert not match(Command(script=script, stderr=''))
@pytest.mark.parametrize('script, branch_name, ', [
('git branch foo', 'foo'), ('git branch bar', 'bar')])
def test_get_new_command(stderr, new_command, script, branch_name):
assert get_new_command(Command(script=script, stderr=stderr)) == new_command

View File

@@ -1,5 +1,5 @@
from thefuck import shells
from thefuck.rules.git_branch_list import match, get_new_command
from thefuck.shells import shell
from tests.utils import Command
@@ -16,4 +16,4 @@ def test_not_match():
def test_get_new_command():
assert (get_new_command(Command('git branch list')) ==
shells.and_('git branch --delete list', 'git branch'))
shell.and_('git branch --delete list', 'git branch'))

View File

@@ -1,5 +1,6 @@
import pytest
from thefuck.rules.git_checkout import match, get_new_command
from io import BytesIO
from thefuck.rules.git_checkout import match, get_branches, get_new_command
from tests.utils import Command
@@ -13,8 +14,10 @@ def did_not_match(target, did_you_forget=False):
@pytest.fixture
def get_branches(mocker):
return mocker.patch('thefuck.rules.git_checkout.get_branches')
def git_branch(mocker, branches):
mock = mocker.patch('subprocess.Popen')
mock.return_value.stdout = BytesIO(branches)
return mock
@pytest.mark.parametrize('command', [
@@ -33,21 +36,34 @@ def test_not_match(command):
assert not match(command)
@pytest.mark.parametrize('branches, branch_list', [
(b'', []),
(b'* master', ['master']),
(b' remotes/origin/master', ['master']),
(b' just-another-branch', ['just-another-branch']),
(b'* master\n just-another-branch', ['master', 'just-another-branch']),
(b'* master\n remotes/origin/master\n just-another-branch',
['master', 'master', 'just-another-branch'])])
def test_get_branches(branches, branch_list, git_branch):
git_branch(branches)
assert list(get_branches()) == branch_list
@pytest.mark.parametrize('branches, command, new_command', [
([],
(b'',
Command(script='git checkout unknown', stderr=did_not_match('unknown')),
'git branch unknown && git checkout unknown'),
([],
(b'',
Command('git commit unknown', stderr=did_not_match('unknown')),
'git branch unknown && git commit unknown'),
(['test-random-branch-123'],
(b' test-random-branch-123',
Command(script='git checkout tst-rdm-brnch-123',
stderr=did_not_match('tst-rdm-brnch-123')),
'git checkout test-random-branch-123'),
(['test-random-branch-123'],
(b' test-random-branch-123',
Command(script='git commit tst-rdm-brnch-123',
stderr=did_not_match('tst-rdm-brnch-123')),
'git commit test-random-branch-123')])
def test_get_new_command(branches, command, new_command, get_branches):
get_branches.return_value = branches
def test_get_new_command(branches, command, new_command, git_branch):
git_branch(branches)
assert get_new_command(command) == new_command

View File

@@ -0,0 +1,24 @@
import pytest
from thefuck.rules.git_help_aliased import match, get_new_command
from tests.utils import Command
@pytest.mark.parametrize('script, stdout', [
('git help st', "`git st' is aliased to `status'"),
('git help ds', "`git ds' is aliased to `diff --staged'")])
def test_match(script, stdout):
assert match(Command(script=script, stdout=stdout))
@pytest.mark.parametrize('script, stdout', [
('git help status', "GIT-STATUS(1)...Git Manual...GIT-STATUS(1)"),
('git help diff', "GIT-DIFF(1)...Git Manual...GIT-DIFF(1)")])
def test_not_match(script, stdout):
assert not match(Command(script=script, stdout=stdout))
@pytest.mark.parametrize('script, stdout, new_command', [
('git help st', "`git st' is aliased to `status'", 'git help status'),
('git help ds', "`git ds' is aliased to `diff --staged'", 'git help diff')])
def test_get_new_command(script, stdout, new_command):
assert get_new_command(Command(script=script, stdout=stdout)) == new_command

View File

@@ -45,8 +45,8 @@ def test_not_match(command):
@pytest.mark.parametrize('command, output', [
(Command(script='git push', stderr=git_err), 'git push --force'),
(Command(script='git push nvbn', stderr=git_err), 'git push --force nvbn'),
(Command(script='git push nvbn master', stderr=git_err), 'git push --force nvbn master')])
(Command(script='git push', stderr=git_err), 'git push --force-with-lease'),
(Command(script='git push nvbn', stderr=git_err), 'git push --force-with-lease nvbn'),
(Command(script='git push nvbn master', stderr=git_err), 'git push --force-with-lease nvbn master')])
def test_get_new_command(command, output):
assert get_new_command(command) == output

View File

@@ -0,0 +1,26 @@
import pytest
from thefuck.rules.git_remote_seturl_add import match, get_new_command
from tests.utils import Command
@pytest.mark.parametrize('command', [
Command(script='git remote set-url origin url', stderr="fatal: No such remote")])
def test_match(command):
assert match(command)
@pytest.mark.parametrize('command', [
Command('git remote set-url origin url', stderr=""),
Command('git remote add origin url'),
Command('git remote remove origin'),
Command('git remote prune origin'),
Command('git remote set-branches origin branch')
])
def test_not_match(command):
assert not match(command)
@pytest.mark.parametrize('command, new_command', [
(Command('git remote set-url origin git@github.com:nvbn/thefuck.git'),
'git remote add origin git@github.com:nvbn/thefuck.git')])
def test_get_new_command(command, new_command):
assert get_new_command(command) == new_command

View File

@@ -0,0 +1,27 @@
import pytest
from thefuck.rules.git_rm_recursive import match, get_new_command
from tests.utils import Command
@pytest.fixture
def stderr(target):
return "fatal: not removing '{}' recursively without -r".format(target)
@pytest.mark.parametrize('script, target', [
('git rm foo', 'foo'),
('git rm foo bar', 'foo bar')])
def test_match(stderr, script, target):
assert match(Command(script=script, stderr=stderr))
@pytest.mark.parametrize('script', ['git rm foo', 'git rm foo bar'])
def test_not_match(script):
assert not match(Command(script=script, stderr=''))
@pytest.mark.parametrize('script, target, new_command', [
('git rm foo', 'foo', 'git rm -r foo'),
('git rm foo bar', 'foo bar', 'git rm -r foo bar')])
def test_get_new_command(stderr, script, target, new_command):
assert get_new_command(Command(script=script, stderr=stderr)) == new_command

View File

@@ -0,0 +1,47 @@
import pytest
from thefuck.rules.git_two_dashes import match, get_new_command
from tests.utils import Command
@pytest.fixture
def stderr(meant):
return 'error: did you mean `%s` (with two dashes ?)' % meant
@pytest.mark.parametrize('command', [
Command(script='git add -patch', stderr=stderr('--patch')),
Command(script='git checkout -patch', stderr=stderr('--patch')),
Command(script='git commit -amend', stderr=stderr('--amend')),
Command(script='git push -tags', stderr=stderr('--tags')),
Command(script='git rebase -continue', stderr=stderr('--continue'))])
def test_match(command):
assert match(command)
@pytest.mark.parametrize('command', [
Command(script='git add --patch'),
Command(script='git checkout --patch'),
Command(script='git commit --amend'),
Command(script='git push --tags'),
Command(script='git rebase --continue')])
def test_not_match(command):
assert not match(command)
@pytest.mark.parametrize('command, output', [
(Command(script='git add -patch', stderr=stderr('--patch')),
'git add --patch'),
(Command(script='git checkout -patch', stderr=stderr('--patch')),
'git checkout --patch'),
(Command(script='git checkout -patch', stderr=stderr('--patch')),
'git checkout --patch'),
(Command(script='git init -bare', stderr=stderr('--bare')),
'git init --bare'),
(Command(script='git commit -amend', stderr=stderr('--amend')),
'git commit --amend'),
(Command(script='git push -tags', stderr=stderr('--tags')),
'git push --tags'),
(Command(script='git rebase -continue', stderr=stderr('--continue')),
'git rebase --continue')])
def test_get_new_command(command, output):
assert get_new_command(command) == output

View File

@@ -0,0 +1,40 @@
import pytest
from thefuck.rules.grep_arguments_order import get_new_command, match
from tests.utils import Command
stderr = 'grep: {}: No such file or directory'.format
@pytest.fixture(autouse=True)
def os_path(monkeypatch):
monkeypatch.setattr('os.path.isfile', lambda x: not x.startswith('-'))
@pytest.mark.parametrize('script, file', [
('grep test.py test', 'test'),
('grep -lir . test', 'test'),
('egrep test.py test', 'test'),
('egrep -lir . test', 'test')])
def test_match(script, file):
assert match(Command(script, stderr=stderr(file)))
@pytest.mark.parametrize('script, stderr', [
('cat test.py', stderr('test')),
('grep test test.py', ''),
('grep -lir test .', ''),
('egrep test test.py', ''),
('egrep -lir test .', '')])
def test_not_match(script, stderr):
assert not match(Command(script, stderr=stderr))
@pytest.mark.parametrize('script, stderr, result', [
('grep test.py test', stderr('test'), 'grep test test.py'),
('grep -lir . test', stderr('test'), 'grep -lir test .'),
('grep . test -lir', stderr('test'), 'grep test -lir .'),
('egrep test.py test', stderr('test'), 'egrep test test.py'),
('egrep -lir . test', stderr('test'), 'egrep -lir test .'),
('egrep . test -lir', stderr('test'), 'egrep test -lir .')])
def test_get_new_command(script, stderr, result):
assert get_new_command(Command(script, stderr=stderr)) == result

View File

@@ -1,12 +1,15 @@
# -*- coding: utf-8 -*-
from thefuck.rules.grep_recursive import match, get_new_command
from tests.utils import Command
def test_match():
assert match(Command('grep blah .', stderr='grep: .: Is a directory'))
assert match(Command(u'grep café .', stderr='grep: .: Is a directory'))
assert not match(Command())
def test_get_new_command():
assert get_new_command(
Command('grep blah .')) == 'grep -r blah .'
assert get_new_command(Command('grep blah .')) == 'grep -r blah .'
assert get_new_command(Command(u'grep café .')) == u'grep -r café .'

View File

@@ -1,4 +1,5 @@
import pytest
from io import BytesIO
from tests.utils import Command
from thefuck.rules.gulp_not_task import match, get_new_command
@@ -22,7 +23,7 @@ def test_not_march(script, stdout):
def test_get_new_command(mocker):
mocker.patch('thefuck.rules.gulp_not_task.get_gulp_tasks', return_value=[
'serve', 'build', 'default'])
mock = mocker.patch('subprocess.Popen')
mock.return_value.stdout = BytesIO(b'serve \nbuild \ndefault \n')
command = Command('gulp srve', stdout('srve'))
assert get_new_command(command) == ['gulp serve', 'gulp default']

View File

@@ -1,17 +1,18 @@
from mock import Mock, patch
from mock import patch
from thefuck.rules.has_exists_script import match, get_new_command
from ..utils import Command
def test_match():
with patch('os.path.exists', return_value=True):
assert match(Mock(script='main', stderr='main: command not found'))
assert match(Mock(script='main --help',
assert match(Command(script='main', stderr='main: command not found'))
assert match(Command(script='main --help',
stderr='main: command not found'))
assert not match(Mock(script='main', stderr=''))
assert not match(Command(script='main', stderr=''))
with patch('os.path.exists', return_value=False):
assert not match(Mock(script='main', stderr='main: command not found'))
assert not match(Command(script='main', stderr='main: command not found'))
def test_get_new_command():
assert get_new_command(Mock(script='main --help')) == './main --help'
assert get_new_command(Command(script='main --help')) == './main --help'

View File

@@ -3,38 +3,23 @@ from thefuck.rules.history import match, get_new_command
from tests.utils import Command
@pytest.fixture
def history(mocker):
return mocker.patch('thefuck.rules.history.get_history',
return_value=['le cat', 'fuck', 'ls cat',
'diff x', 'nocommand x'])
@pytest.fixture(autouse=True)
def history_without_current(mocker):
return mocker.patch(
'thefuck.rules.history.get_valid_history_without_current',
return_value=['ls cat', 'diff x'])
@pytest.fixture
def alias(mocker):
return mocker.patch('thefuck.rules.history.thefuck_alias',
return_value='fuck')
@pytest.fixture
def callables(mocker):
return mocker.patch('thefuck.rules.history.get_all_executables',
return_value=['diff', 'ls'])
@pytest.mark.usefixtures('history', 'callables', 'no_memoize', 'alias')
@pytest.mark.parametrize('script', ['ls cet', 'daff x'])
def test_match(script):
assert match(Command(script=script))
@pytest.mark.usefixtures('history', 'callables', 'no_memoize', 'alias')
@pytest.mark.parametrize('script', ['apt-get', 'nocommand y'])
def test_not_match(script):
assert not match(Command(script=script))
@pytest.mark.usefixtures('history', 'callables', 'no_memoize', 'alias')
@pytest.mark.parametrize('script, result', [
('ls cet', 'ls cat'),
('daff x', 'diff x')])

View File

@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
import pytest
from thefuck.rules.ln_no_hard_link import match, get_new_command
from tests.utils import Command
error = "hard link not allowed for directory"
@pytest.mark.parametrize('script, stderr', [
("ln barDir barLink", "ln: barDir: {}"),
("sudo ln a b", "ln: a: {}"),
("sudo ln -nbi a b", "ln: a: {}")])
def test_match(script, stderr):
command = Command(script, stderr=stderr.format(error))
assert match(command)
@pytest.mark.parametrize('script, stderr', [
('', ''),
("ln a b", "... hard link"),
("sudo ln a b", "... hard link"),
("a b", error)])
def test_assert_not_match(script, stderr):
command = Command(script, stderr=stderr)
assert not match(command)
@pytest.mark.parametrize('script, result', [
("ln barDir barLink", "ln -s barDir barLink"),
("sudo ln barDir barLink", "sudo ln -s barDir barLink"),
("sudo ln -nbi a b", "sudo ln -s -nbi a b"),
("ln -nbi a b && ls", "ln -s -nbi a b && ls"),
("ln a ln", "ln -s a ln"),
("sudo ln a ln", "sudo ln -s a ln")])
def test_get_new_command(script, result):
command = Command(script)
assert get_new_command(command) == result

View File

@@ -7,7 +7,7 @@ from tests.utils import Command
Command('mkdir foo/bar/baz', stderr='mkdir: foo/bar: No such file or directory'),
Command('./bin/hdfs dfs -mkdir foo/bar/baz', stderr='mkdir: `foo/bar/baz\': No such file or directory'),
Command('hdfs dfs -mkdir foo/bar/baz', stderr='mkdir: `foo/bar/baz\': No such file or directory')
])
])
def test_match(command):
assert match(command)
@@ -17,7 +17,8 @@ def test_match(command):
Command('mkdir foo/bar/baz', stderr='foo bar baz'),
Command('hdfs dfs -mkdir foo/bar/baz'),
Command('./bin/hdfs dfs -mkdir foo/bar/baz'),
Command()])
Command(),
])
def test_not_match(command):
assert not match(command)
@@ -25,7 +26,7 @@ def test_not_match(command):
@pytest.mark.parametrize('command, new_command', [
(Command('mkdir foo/bar/baz'), 'mkdir -p foo/bar/baz'),
(Command('hdfs dfs -mkdir foo/bar/baz'), 'hdfs dfs -mkdir -p foo/bar/baz'),
(Command('./bin/hdfs dfs -mkdir foo/bar/baz'), './bin/hdfs dfs -mkdir -p foo/bar/baz')])
(Command('./bin/hdfs dfs -mkdir foo/bar/baz'), './bin/hdfs dfs -mkdir -p foo/bar/baz'),
])
def test_get_new_command(command, new_command):
assert get_new_command(command) == new_command

View File

@@ -32,9 +32,9 @@ def test_match(command):
def test_not_match(command):
assert not match(command)
@pytest.mark.parametrize('command, new_command', [
(Command(script='mvn', stdout='[ERROR] No goals have been specified for this build. You must specify a valid lifecycle phase or a goal in the format <plugin-prefix>:<goal> or <plugin-group-id>:<plugin-artifact-id>[:<plugin-version>]:<goal>. Available lifecycle phases are: validate, initialize, generate-sources, process-sources, generate-resources, process-resources, compile, process-classes, generate-test-sources, process-test-sources, generate-test-resources, process-test-resources, test-compile, process-test-classes, test, prepare-package, package, pre-integration-test, integration-test, post-integration-test, verify, install, deploy, pre-clean, clean, post-clean, pre-site, site, post-site, site-deploy. -> [Help 1]'), ['mvn clean package', 'mvn clean install']),
(Command(script='mvn -N', stdout='[ERROR] No goals have been specified for this build. You must specify a valid lifecycle phase or a goal in the format <plugin-prefix>:<goal> or <plugin-group-id>:<plugin-artifact-id>[:<plugin-version>]:<goal>. Available lifecycle phases are: validate, initialize, generate-sources, process-sources, generate-resources, process-resources, compile, process-classes, generate-test-sources, process-test-sources, generate-test-resources, process-test-resources, test-compile, process-test-classes, test, prepare-package, package, pre-integration-test, integration-test, post-integration-test, verify, install, deploy, pre-clean, clean, post-clean, pre-site, site, post-site, site-deploy. -> [Help 1]'), ['mvn -N clean package', 'mvn -N clean install'])])
def test_get_new_command(command, new_command):
assert get_new_command(command) == new_command

View File

@@ -32,9 +32,9 @@ def test_match(command):
def test_not_match(command):
assert not match(command)
@pytest.mark.parametrize('command, new_command', [
(Command(script='mvn cle', stdout='[ERROR] Unknown lifecycle phase "cle". You must specify a valid lifecycle phase or a goal in the format <plugin-prefix>:<goal> or <plugin-group-id>:<plugin-artifact-id>[:<plugin-version>]:<goal>. Available lifecycle phases are: validate, initialize, generate-sources, process-sources, generate-resources, process-resources, compile, process-classes, generate-test-sources, process-test-sources, generate-test-resources, process-test-resources, test-compile, process-test-classes, test, prepare-package, package, pre-integration-test, integration-test, post-integration-test, verify, install, deploy, pre-clean, clean, post-clean, pre-site, site, post-site, site-deploy. -> [Help 1]'), ['mvn clean', 'mvn compile']),
(Command(script='mvn claen package', stdout='[ERROR] Unknown lifecycle phase "claen". You must specify a valid lifecycle phase or a goal in the format <plugin-prefix>:<goal> or <plugin-group-id>:<plugin-artifact-id>[:<plugin-version>]:<goal>. Available lifecycle phases are: validate, initialize, generate-sources, process-sources, generate-resources, process-resources, compile, process-classes, generate-test-sources, process-test-sources, generate-test-resources, process-test-resources, test-compile, process-test-classes, test, prepare-package, package, pre-integration-test, integration-test, post-integration-test, verify, install, deploy, pre-clean, clean, post-clean, pre-site, site, post-site, site-deploy. -> [Help 1]'), ['mvn clean package'])])
def test_get_new_command(command, new_command):
assert get_new_command(command) == new_command

View File

@@ -6,22 +6,37 @@ from tests.utils import Command
@pytest.fixture(autouse=True)
def get_all_executables(mocker):
mocker.patch('thefuck.rules.no_command.get_all_executables',
return_value=['vim', 'apt-get', 'fsck'])
return_value=['vim', 'fsck', 'git', 'go'])
@pytest.fixture(autouse=True)
def history_without_current(mocker):
return mocker.patch(
'thefuck.rules.no_command.get_valid_history_without_current',
return_value=['git commit'])
@pytest.mark.usefixtures('no_memoize')
def test_match():
assert match(Command(stderr='vom: not found', script='vom file.py'))
assert match(Command(stderr='fucck: not found', script='fucck'))
assert not match(Command(stderr='qweqwe: not found', script='qweqwe'))
assert not match(Command(stderr='some text', script='vom file.py'))
@pytest.mark.parametrize('script, stderr', [
('vom file.py', 'vom: not found'),
('fucck', 'fucck: not found'),
('got commit', 'got: command not found')])
def test_match(script, stderr):
assert match(Command(script, stderr=stderr))
@pytest.mark.usefixtures('no_memoize')
def test_get_new_command():
assert get_new_command(
Command(stderr='vom: not found',
script='vom file.py')) == ['vim file.py']
assert get_new_command(
Command(stderr='fucck: not found',
script='fucck')) == ['fsck']
@pytest.mark.parametrize('script, stderr', [
('qweqwe', 'qweqwe: not found'),
('vom file.py', 'some text')])
def test_not_match(script, stderr):
assert not match(Command(script, stderr=stderr))
@pytest.mark.usefixtures('no_memoize')
@pytest.mark.parametrize('script, result', [
('vom file.py', ['vim file.py']),
('fucck', ['fsck']),
('got commit', ['git commit', 'go commit'])])
def test_get_new_command(script, result):
assert get_new_command(Command(script)) == result

View File

@@ -6,7 +6,7 @@ from tests.utils import Command
@pytest.mark.parametrize('command', [
Command(script='mv foo bar/foo', stderr="mv: cannot move 'foo' to 'bar/foo': No such file or directory"),
Command(script='mv foo bar/', stderr="mv: cannot move 'foo' to 'bar/': No such file or directory"),
])
])
def test_match(command):
assert match(command)
@@ -14,7 +14,7 @@ def test_match(command):
@pytest.mark.parametrize('command', [
Command(script='mv foo bar/', stderr=""),
Command(script='mv foo bar/foo', stderr="mv: permission denied"),
])
])
def test_not_match(command):
assert not match(command)
@@ -22,6 +22,6 @@ def test_not_match(command):
@pytest.mark.parametrize('command, new_command', [
(Command(script='mv foo bar/foo', stderr="mv: cannot move 'foo' to 'bar/foo': No such file or directory"), 'mkdir -p bar && mv foo bar/foo'),
(Command(script='mv foo bar/', stderr="mv: cannot move 'foo' to 'bar/': No such file or directory"), 'mkdir -p bar && mv foo bar/'),
])
])
def test_get_new_command(command, new_command):
assert get_new_command(command) == new_command

View File

@@ -0,0 +1,58 @@
import pytest
from thefuck.rules.npm_wrong_command import match, get_new_command
from tests.utils import Command
stdout = '''
Usage: npm <command>
where <command> is one of:
access, add-user, adduser, apihelp, author, bin, bugs, c,
cache, completion, config, ddp, dedupe, deprecate, dist-tag,
dist-tags, docs, edit, explore, faq, find, find-dupes, get,
help, help-search, home, i, info, init, install, issues, la,
link, list, ll, ln, login, logout, ls, outdated, owner,
pack, ping, prefix, prune, publish, r, rb, rebuild, remove,
repo, restart, rm, root, run-script, s, se, search, set,
show, shrinkwrap, star, stars, start, stop, t, tag, team,
test, tst, un, uninstall, unlink, unpublish, unstar, up,
update, upgrade, v, verison, version, view, whoami
npm <cmd> -h quick help on <cmd>
npm -l display full usage info
npm faq commonly asked questions
npm help <term> search for help on <term>
npm help npm involved overview
Specify configs in the ini-formatted file:
/home/nvbn/.npmrc
or on the command line via: npm <command> --key value
Config info can be viewed via: npm help config
npm@2.14.7 /opt/node/lib/node_modules/npm
'''
@pytest.mark.parametrize('script', [
'npm urgrdae',
'npm urgrade -g',
'npm -f urgrade -g',
'npm urg'])
def test_match(script):
assert match(Command(script, stdout))
@pytest.mark.parametrize('script, stdout', [
('npm urgrade', ''),
('npm', stdout),
('test urgrade', stdout),
('npm -e', stdout)])
def test_not_match(script, stdout):
assert not match(Command(script, stdout))
@pytest.mark.parametrize('script, result', [
('npm urgrade', 'npm upgrade'),
('npm -g isntall gulp', 'npm -g install gulp'),
('npm isntall -g gulp', 'npm install -g gulp')])
def test_get_new_command(script, result):
assert get_new_command(Command(script, stdout)) == result

View File

@@ -7,25 +7,25 @@ from tests.utils import Command
Command('rm foo', stderr='rm: foo: is a directory'),
Command('rm foo', stderr='rm: foo: Is a directory'),
Command('hdfs dfs -rm foo', stderr='rm: `foo`: Is a directory'),
Command('./bin/hdfs dfs -rm foo', stderr='rm: `foo`: Is a directory')
])
Command('./bin/hdfs dfs -rm foo', stderr='rm: `foo`: Is a directory'),
])
def test_match(command):
assert match(command)
@pytest.mark.parametrize('command', [
Command('rm foo'),
Command('rm foo'),
Command('hdfs dfs -rm foo'),
Command('./bin/hdfs dfs -rm foo'),
Command()])
Command('./bin/hdfs dfs -rm foo'),
Command(),
])
def test_not_match(command):
assert not match(command)
@pytest.mark.parametrize('command, new_command', [
(Command('rm foo'), 'rm -rf foo'),
(Command('hdfs dfs -rm foo'), 'hdfs dfs -rm -r foo')])
(Command('hdfs dfs -rm foo'), 'hdfs dfs -rm -r foo'),
])
def test_get_new_command(command, new_command):
assert get_new_command(command) == new_command

View File

@@ -1,6 +1,5 @@
import os
import pytest
from mock import Mock
from thefuck.rules.ssh_known_hosts import match, get_new_command,\
side_effect
from tests.utils import Command
@@ -53,6 +52,7 @@ def test_match(ssh_error):
assert not match(Command('ssh'))
@pytest.mark.skipif(os.name == 'nt', reason='Skip if testing on Windows')
def test_side_effect(ssh_error):
errormsg, path, reset, known_hosts = ssh_error
command = Command('ssh user@host', stderr=errormsg)

View File

@@ -19,11 +19,13 @@ def test_match(stderr, stdout):
def test_not_match():
assert not match(Command())
assert not match(Command(script='sudo ls', stderr='Permission denied'))
@pytest.mark.parametrize('before, after', [
('ls', 'sudo ls'),
('echo a > b', 'sudo sh -c "echo a > b"'),
('echo "a" >> b', 'sudo sh -c "echo \\"a\\" >> b"')])
('echo "a" >> b', 'sudo sh -c "echo \\"a\\" >> b"'),
('mkdir && touch a', 'sudo sh -c "mkdir && touch a"')])
def test_get_new_command(before, after):
assert get_new_command(Command(before)) == after

View File

@@ -1,4 +1,3 @@
import pytest
from thefuck.rules.systemctl import match, get_new_command
from tests.utils import Command

View File

@@ -14,8 +14,8 @@ def test_match(command):
@pytest.mark.parametrize('command', [
Command(script='./bin/hdfs dfs -ls', stderr=''),
Command(script='./bin/hdfs dfs -ls /foo/bar', stderr=''),
Command(script='hdfs dfs -ls -R /foo/bar', stderr=''),
Command(script='./bin/hdfs dfs -ls /foo/bar', stderr=''),
Command(script='hdfs dfs -ls -R /foo/bar', stderr=''),
Command()])
def test_not_match(command):
assert not match(command)
@@ -32,4 +32,3 @@ def test_not_match(command):
stderr='ls: Unknown command\nDid you mean -ls? This command begins with a dash.'), ['./bin/hdfs dfs -Dtest=fred -ls -R /foo/bar'])])
def test_get_new_command(command, new_command):
assert get_new_command(command) == new_command

View File

@@ -16,8 +16,8 @@ def test_match(command):
@pytest.mark.parametrize('command', [
Command(script='vagrant ssh', stderr=''),
Command(script='vagrant ssh jeff', stderr='The machine with the name \'jeff\' was not found configured for this Vagrant environment.'),
Command(script='vagrant ssh', stderr='A Vagrant environment or target machine is required to run this command. Run `vagrant init` to create a new Vagrant environment. Or, get an ID of a target machine from `vagrant global-status` to run this command on. A final option is to change to a directory with a Vagrantfile and to try again.'),
Command(script='vagrant ssh jeff', stderr='The machine with the name \'jeff\' was not found configured for this Vagrant environment.'),
Command(script='vagrant ssh', stderr='A Vagrant environment or target machine is required to run this command. Run `vagrant init` to create a new Vagrant environment. Or, get an ID of a target machine from `vagrant global-status` to run this command on. A final option is to change to a directory with a Vagrantfile and to try again.'),
Command()])
def test_not_match(command):
assert not match(command)
@@ -32,4 +32,3 @@ def test_not_match(command):
stderr='VM must be created before running this command. Run `vagrant up` first.'), ['vagrant up devbox && vagrant rdp devbox', 'vagrant up && vagrant rdp devbox'])])
def test_get_new_command(command, new_command):
assert get_new_command(command) == new_command

0
tests/shells/__init__.py Normal file
View File

22
tests/shells/conftest.py Normal file
View File

@@ -0,0 +1,22 @@
import pytest
@pytest.fixture
def builtins_open(mocker):
return mocker.patch('six.moves.builtins.open')
@pytest.fixture
def isfile(mocker):
return mocker.patch('os.path.isfile', return_value=True)
@pytest.fixture
@pytest.mark.usefixtures('isfile')
def history_lines(mocker):
def aux(lines):
mock = mocker.patch('io.open')
mock.return_value.__enter__ \
.return_value.readlines.return_value = lines
return aux

58
tests/shells/test_bash.py Normal file
View File

@@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
import os
import pytest
from thefuck.shells import Bash
@pytest.mark.usefixtures('isfile', 'no_memoize', 'no_cache')
class TestBash(object):
@pytest.fixture
def shell(self):
return Bash()
@pytest.fixture(autouse=True)
def shell_aliases(self):
os.environ['TF_SHELL_ALIASES'] = (
'alias fuck=\'eval $(thefuck $(fc -ln -1))\'\n'
'alias l=\'ls -CF\'\n'
'alias la=\'ls -A\'\n'
'alias ll=\'ls -alF\'')
@pytest.mark.parametrize('before, after', [
('pwd', 'pwd'),
('fuck', 'eval $(thefuck $(fc -ln -1))'),
('awk', 'awk'),
('ll', 'ls -alF')])
def test_from_shell(self, before, after, shell):
assert shell.from_shell(before) == after
def test_to_shell(self, shell):
assert shell.to_shell('pwd') == 'pwd'
def test_and_(self, shell):
assert shell.and_('ls', 'cd') == 'ls && cd'
def test_get_aliases(self, shell):
assert shell.get_aliases() == {'fuck': 'eval $(thefuck $(fc -ln -1))',
'l': 'ls -CF',
'la': 'ls -A',
'll': 'ls -alF'}
def test_app_alias(self, shell):
assert 'alias fuck' in shell.app_alias('fuck')
assert 'alias FUCK' in shell.app_alias('FUCK')
assert 'thefuck' in shell.app_alias('fuck')
assert 'TF_ALIAS=fuck' in shell.app_alias('fuck')
assert 'PYTHONIOENCODING=utf-8' in shell.app_alias('fuck')
def test_app_alias_variables_correctly_set(self, shell):
alias = shell.app_alias('fuck')
assert "alias fuck='TF_CMD=$(TF_ALIAS" in alias
assert '$(TF_ALIAS=fuck PYTHONIOENCODING' in alias
assert 'PYTHONIOENCODING=utf-8 TF_SHELL_ALIASES' in alias
assert 'ALIASES=$(alias) thefuck' in alias
def test_get_history(self, history_lines, shell):
history_lines(['ls', 'rm'])
assert list(shell.get_history()) == ['ls', 'rm']

80
tests/shells/test_fish.py Normal file
View File

@@ -0,0 +1,80 @@
# -*- coding: utf-8 -*-
import pytest
from thefuck.shells import Fish
@pytest.mark.usefixtures('isfile', 'no_memoize', 'no_cache')
class TestFish(object):
@pytest.fixture
def shell(self):
return Fish()
@pytest.fixture(autouse=True)
def Popen(self, mocker):
mock = mocker.patch('thefuck.shells.fish.Popen')
mock.return_value.stdout.read.return_value = (
b'cd\nfish_config\nfuck\nfunced\nfuncsave\ngrep\nhistory\nll\nls\n'
b'man\nmath\npopd\npushd\nruby')
return mock
@pytest.fixture
def os_environ(self, monkeypatch, key, value):
monkeypatch.setattr('os.environ', {key: value})
@pytest.mark.parametrize('key, value', [
('TF_OVERRIDDEN_ALIASES', 'cut,git,sed'), # legacy
('THEFUCK_OVERRIDDEN_ALIASES', 'cut,git,sed'),
('THEFUCK_OVERRIDDEN_ALIASES', 'cut, git, sed'),
('THEFUCK_OVERRIDDEN_ALIASES', ' cut,\tgit,sed\n'),
('THEFUCK_OVERRIDDEN_ALIASES', '\ncut,\n\ngit,\tsed\r')])
def test_get_overridden_aliases(self, shell, os_environ):
assert shell._get_overridden_aliases() == {'cd', 'cut', 'git', 'grep',
'ls', 'man', 'open', 'sed'}
@pytest.mark.parametrize('before, after', [
('cd', 'cd'),
('pwd', 'pwd'),
('fuck', 'fish -ic "fuck"'),
('find', 'find'),
('funced', 'fish -ic "funced"'),
('grep', 'grep'),
('awk', 'awk'),
('math "2 + 2"', r'fish -ic "math \"2 + 2\""'),
('man', 'man'),
('open', 'open'),
('vim', 'vim'),
('ll', 'fish -ic "ll"'),
('ls', 'ls')]) # Fish has no aliases but functions
def test_from_shell(self, before, after, shell):
assert shell.from_shell(before) == after
def test_to_shell(self, shell):
assert shell.to_shell('pwd') == 'pwd'
def test_and_(self, shell):
assert shell.and_('foo', 'bar') == 'foo; and bar'
def test_get_aliases(self, shell):
assert shell.get_aliases() == {'fish_config': 'fish_config',
'fuck': 'fuck',
'funced': 'funced',
'funcsave': 'funcsave',
'history': 'history',
'll': 'll',
'math': 'math',
'popd': 'popd',
'pushd': 'pushd',
'ruby': 'ruby'}
def test_app_alias(self, shell):
assert 'function fuck' in shell.app_alias('fuck')
assert 'function FUCK' in shell.app_alias('FUCK')
assert 'thefuck' in shell.app_alias('fuck')
assert 'TF_ALIAS=fuck PYTHONIOENCODING' in shell.app_alias('fuck')
assert 'PYTHONIOENCODING=utf-8 thefuck' in shell.app_alias('fuck')
def test_get_history(self, history_lines, shell):
history_lines(['- cmd: ls', ' when: 1432613911',
'- cmd: rm', ' when: 1432613916'])
assert list(shell.get_history()) == ['ls', 'rm']

View File

@@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
import pytest
from thefuck.shells import Generic
class TestGeneric(object):
@pytest.fixture
def shell(self):
return Generic()
def test_from_shell(self, shell):
assert shell.from_shell('pwd') == 'pwd'
def test_to_shell(self, shell):
assert shell.to_shell('pwd') == 'pwd'
def test_and_(self, shell):
assert shell.and_('ls', 'cd') == 'ls && cd'
def test_get_aliases(self, shell):
assert shell.get_aliases() == {}
def test_app_alias(self, shell):
assert 'alias fuck' in shell.app_alias('fuck')
assert 'alias FUCK' in shell.app_alias('FUCK')
assert 'thefuck' in shell.app_alias('fuck')
assert 'TF_ALIAS=fuck PYTHONIOENCODING' in shell.app_alias('fuck')
assert 'PYTHONIOENCODING=utf-8 thefuck' in shell.app_alias('fuck')
def test_get_history(self, history_lines, shell):
history_lines(['ls', 'rm'])
# We don't know what to do in generic shell with history lines,
# so just ignore them:
assert list(shell.get_history()) == []
def test_split_command(self, shell):
assert shell.split_command('ls') == ['ls']
assert shell.split_command(u'echo café') == [u'echo', u'café']

View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
import pytest
from thefuck.shells import Powershell
@pytest.mark.usefixtures('isfile', 'no_memoize', 'no_cache')
class TestPowershell(object):
@pytest.fixture
def shell(self):
return Powershell()
def test_and_(self, shell):
assert shell.and_('ls', 'cd') == '(ls) -and (cd)'
def test_app_alias(self, shell):
assert 'function fuck' in shell.app_alias('fuck')
assert 'function FUCK' in shell.app_alias('FUCK')
assert 'thefuck' in shell.app_alias('fuck')

50
tests/shells/test_tcsh.py Normal file
View File

@@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
import pytest
from thefuck.shells.tcsh import Tcsh
@pytest.mark.usefixtures('isfile', 'no_memoize', 'no_cache')
class TestTcsh(object):
@pytest.fixture
def shell(self):
return Tcsh()
@pytest.fixture(autouse=True)
def Popen(self, mocker):
mock = mocker.patch('thefuck.shells.tcsh.Popen')
mock.return_value.stdout.read.return_value = (
b'fuck\teval $(thefuck $(fc -ln -1))\n'
b'l\tls -CF\n'
b'la\tls -A\n'
b'll\tls -alF')
return mock
@pytest.mark.parametrize('before, after', [
('pwd', 'pwd'),
('fuck', 'eval $(thefuck $(fc -ln -1))'),
('awk', 'awk'),
('ll', 'ls -alF')])
def test_from_shell(self, before, after, shell):
assert shell.from_shell(before) == after
def test_to_shell(self, shell):
assert shell.to_shell('pwd') == 'pwd'
def test_and_(self, shell):
assert shell.and_('ls', 'cd') == 'ls && cd'
def test_get_aliases(self, shell):
assert shell.get_aliases() == {'fuck': 'eval $(thefuck $(fc -ln -1))',
'l': 'ls -CF',
'la': 'ls -A',
'll': 'ls -alF'}
def test_app_alias(self, shell):
assert 'alias fuck' in shell.app_alias('fuck')
assert 'alias FUCK' in shell.app_alias('FUCK')
assert 'thefuck' in shell.app_alias('fuck')
def test_get_history(self, history_lines, shell):
history_lines(['ls', 'rm'])
assert list(shell.get_history()) == ['ls', 'rm']

57
tests/shells/test_zsh.py Normal file
View File

@@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-
import os
import pytest
from thefuck.shells.zsh import Zsh
@pytest.mark.usefixtures('isfile', 'no_memoize', 'no_cache')
class TestZsh(object):
@pytest.fixture
def shell(self):
return Zsh()
@pytest.fixture(autouse=True)
def shell_aliases(self):
os.environ['TF_SHELL_ALIASES'] = (
'fuck=\'eval $(thefuck $(fc -ln -1 | tail -n 1))\'\n'
'l=\'ls -CF\'\n'
'la=\'ls -A\'\n'
'll=\'ls -alF\'')
@pytest.mark.parametrize('before, after', [
('fuck', 'eval $(thefuck $(fc -ln -1 | tail -n 1))'),
('pwd', 'pwd'),
('ll', 'ls -alF')])
def test_from_shell(self, before, after, shell):
assert shell.from_shell(before) == after
def test_to_shell(self, shell):
assert shell.to_shell('pwd') == 'pwd'
def test_and_(self, shell):
assert shell.and_('ls', 'cd') == 'ls && cd'
def test_get_aliases(self, shell):
assert shell.get_aliases() == {
'fuck': 'eval $(thefuck $(fc -ln -1 | tail -n 1))',
'l': 'ls -CF',
'la': 'ls -A',
'll': 'ls -alF'}
def test_app_alias(self, shell):
assert 'alias fuck' in shell.app_alias('fuck')
assert 'alias FUCK' in shell.app_alias('FUCK')
assert 'thefuck' in shell.app_alias('fuck')
assert 'PYTHONIOENCODING' in shell.app_alias('fuck')
def test_app_alias_variables_correctly_set(self, shell):
alias = shell.app_alias('fuck')
assert "alias fuck='TF_CMD=$(TF_ALIAS" in alias
assert '$(TF_ALIAS=fuck PYTHONIOENCODING' in alias
assert 'PYTHONIOENCODING=utf-8 TF_SHELL_ALIASES' in alias
assert 'ALIASES=$(alias) thefuck' in alias
def test_get_history(self, history_lines, shell):
history_lines([': 1432613911:0;ls', ': 1432613916:0;rm'])
assert list(shell.get_history()) == ['ls', 'rm']

View File

@@ -1,5 +1,4 @@
import pytest
from mock import Mock
from thefuck.specific.sudo import sudo_support
from tests.utils import Command

View File

@@ -1,7 +1,7 @@
import pytest
import six
from mock import Mock
from thefuck import conf
from thefuck import const
@pytest.fixture
@@ -20,7 +20,7 @@ def environ(monkeypatch):
def test_settings_defaults(load_source, settings):
load_source.return_value = object()
settings.init()
for key, val in conf.DEFAULT_SETTINGS.items():
for key, val in const.DEFAULT_SETTINGS.items():
assert getattr(settings, key) == val
@@ -42,13 +42,13 @@ class TestSettingsFromFile(object):
assert settings.exclude_rules == ['git']
def test_from_file_with_DEFAULT(self, load_source, settings):
load_source.return_value = Mock(rules=conf.DEFAULT_RULES + ['test'],
load_source.return_value = Mock(rules=const.DEFAULT_RULES + ['test'],
wait_command=10,
exclude_rules=[],
require_confirmation=True,
no_colors=True)
settings.init()
assert settings.rules == conf.DEFAULT_RULES + ['test']
assert settings.rules == const.DEFAULT_RULES + ['test']
@pytest.mark.usefixture('load_source')
@@ -71,7 +71,7 @@ class TestSettingsFromEnv(object):
def test_from_env_with_DEFAULT(self, environ, settings):
environ.update({'THEFUCK_RULES': 'DEFAULT_RULES:bash:lisp'})
settings.init()
assert settings.rules == conf.DEFAULT_RULES + ['bash', 'lisp']
assert settings.rules == const.DEFAULT_RULES + ['bash', 'lisp']
class TestInitializeSettingsFile(object):
@@ -93,7 +93,7 @@ class TestInitializeSettingsFile(object):
settings_file_contents = settings_file.getvalue()
assert settings_path_mock.is_file.call_count == 1
assert settings_path_mock.open.call_count == 1
assert conf.SETTINGS_HEADER in settings_file_contents
for setting in conf.DEFAULT_SETTINGS.items():
assert const.SETTINGS_HEADER in settings_file_contents
for setting in const.DEFAULT_SETTINGS.items():
assert '# {} = {}\n'.format(*setting) in settings_file_contents
settings_file.close()

View File

@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
import pytest
from pathlib import PosixPath
from thefuck import corrector, conf
from pathlib import Path
from thefuck import corrector, const
from tests.utils import Rule, Command, CorrectedCommand
from thefuck.corrector import get_corrected_commands, organize_commands
@@ -22,13 +24,13 @@ class TestGetRules(object):
assert {r.name for r in rules} == set(names)
@pytest.mark.parametrize('paths, conf_rules, exclude_rules, loaded_rules', [
(['git.py', 'bash.py'], conf.DEFAULT_RULES, [], ['git', 'bash']),
(['git.py', 'bash.py'], const.DEFAULT_RULES, [], ['git', 'bash']),
(['git.py', 'bash.py'], ['git'], [], ['git']),
(['git.py', 'bash.py'], conf.DEFAULT_RULES, ['git'], ['bash']),
(['git.py', 'bash.py'], const.DEFAULT_RULES, ['git'], ['bash']),
(['git.py', 'bash.py'], ['git'], ['git'], [])])
def test_get_rules(self, glob, settings, paths, conf_rules, exclude_rules,
loaded_rules):
glob([PosixPath(path) for path in paths])
glob([Path(path) for path in paths])
settings.update(rules=conf_rules,
priority={},
exclude_rules=exclude_rules)
@@ -53,7 +55,9 @@ def test_organize_commands():
"""Ensures that the function removes duplicates and sorts commands."""
commands = [CorrectedCommand('ls'), CorrectedCommand('ls -la', priority=9000),
CorrectedCommand('ls -lh', priority=100),
CorrectedCommand(u'echo café', priority=200),
CorrectedCommand('ls -lh', priority=9999)]
assert list(organize_commands(iter(commands))) \
== [CorrectedCommand('ls'), CorrectedCommand('ls -lh', priority=100),
CorrectedCommand(u'echo café', priority=200),
CorrectedCommand('ls -la', priority=9000)]

View File

@@ -1,5 +1,4 @@
import pytest
from mock import Mock
from thefuck import logs

View File

@@ -1,235 +0,0 @@
import pytest
from thefuck import shells
@pytest.fixture
def builtins_open(mocker):
return mocker.patch('six.moves.builtins.open')
@pytest.fixture
def isfile(mocker):
return mocker.patch('os.path.isfile', return_value=True)
@pytest.fixture
@pytest.mark.usefixtures('isfile')
def history_lines(mocker):
def aux(lines):
mock = mocker.patch('io.open')
mock.return_value.__enter__\
.return_value.__iter__.return_value = lines
return aux
class TestGeneric(object):
@pytest.fixture
def shell(self):
return shells.Generic()
def test_from_shell(self, shell):
assert shell.from_shell('pwd') == 'pwd'
def test_to_shell(self, shell):
assert shell.to_shell('pwd') == 'pwd'
def test_put_to_history(self, builtins_open, shell):
assert shell.put_to_history('ls') is None
assert builtins_open.call_count == 0
def test_and_(self, shell):
assert shell.and_('ls', 'cd') == 'ls && cd'
def test_get_aliases(self, shell):
assert shell.get_aliases() == {}
def test_app_alias(self, shell):
assert 'alias fuck' in shell.app_alias('fuck')
assert 'alias FUCK' in shell.app_alias('FUCK')
assert 'thefuck' in shell.app_alias('fuck')
assert 'TF_ALIAS' in shell.app_alias('fuck')
def test_get_history(self, history_lines, shell):
history_lines(['ls', 'rm'])
# We don't know what to do in generic shell with history lines,
# so just ignore them:
assert list(shell.get_history()) == []
@pytest.mark.usefixtures('isfile')
class TestBash(object):
@pytest.fixture
def shell(self):
return shells.Bash()
@pytest.fixture(autouse=True)
def Popen(self, mocker):
mock = mocker.patch('thefuck.shells.Popen')
mock.return_value.stdout.read.return_value = (
b'alias fuck=\'eval $(thefuck $(fc -ln -1))\'\n'
b'alias l=\'ls -CF\'\n'
b'alias la=\'ls -A\'\n'
b'alias ll=\'ls -alF\'')
return mock
@pytest.mark.parametrize('before, after', [
('pwd', 'pwd'),
('fuck', 'eval $(thefuck $(fc -ln -1))'),
('awk', 'awk'),
('ll', 'ls -alF')])
def test_from_shell(self, before, after, shell):
assert shell.from_shell(before) == after
def test_to_shell(self, shell):
assert shell.to_shell('pwd') == 'pwd'
def test_put_to_history(self, builtins_open, shell):
shell.put_to_history('ls')
builtins_open.return_value.__enter__.return_value. \
write.assert_called_once_with('ls\n')
def test_and_(self, shell):
assert shell.and_('ls', 'cd') == 'ls && cd'
def test_get_aliases(self, shell):
assert shell.get_aliases() == {'fuck': 'eval $(thefuck $(fc -ln -1))',
'l': 'ls -CF',
'la': 'ls -A',
'll': 'ls -alF'}
def test_app_alias(self, shell):
assert 'alias fuck' in shell.app_alias('fuck')
assert 'alias FUCK' in shell.app_alias('FUCK')
assert 'thefuck' in shell.app_alias('fuck')
assert 'TF_ALIAS' in shell.app_alias('fuck')
def test_get_history(self, history_lines, shell):
history_lines(['ls', 'rm'])
assert list(shell.get_history()) == ['ls', 'rm']
@pytest.mark.usefixtures('isfile')
class TestFish(object):
@pytest.fixture
def shell(self):
return shells.Fish()
@pytest.fixture(autouse=True)
def Popen(self, mocker):
mock = mocker.patch('thefuck.shells.Popen')
mock.return_value.stdout.read.return_value = (
b'cd\nfish_config\nfuck\nfunced\nfuncsave\ngrep\nhistory\nll\nls\n'
b'man\nmath\npopd\npushd\nruby')
return mock
@pytest.fixture
def environ(self, monkeypatch):
data = {'TF_OVERRIDDEN_ALIASES': 'cd, ls, man, open'}
monkeypatch.setattr('thefuck.shells.os.environ', data)
return data
@pytest.mark.usefixture('environ')
def test_get_overridden_aliases(self, shell, environ):
assert shell._get_overridden_aliases() == ['cd', 'ls', 'man', 'open']
@pytest.mark.parametrize('before, after', [
('cd', 'cd'),
('pwd', 'pwd'),
('fuck', 'fish -ic "fuck"'),
('find', 'find'),
('funced', 'fish -ic "funced"'),
('grep', 'grep'),
('awk', 'awk'),
('math "2 + 2"', r'fish -ic "math \"2 + 2\""'),
('man', 'man'),
('open', 'open'),
('vim', 'vim'),
('ll', 'fish -ic "ll"'),
('ls', 'ls')]) # Fish has no aliases but functions
def test_from_shell(self, before, after, shell):
assert shell.from_shell(before) == after
def test_to_shell(self, shell):
assert shell.to_shell('pwd') == 'pwd'
def test_put_to_history(self, builtins_open, mocker, shell):
mocker.patch('thefuck.shells.time',
return_value=1430707243.3517463)
shell.put_to_history('ls')
builtins_open.return_value.__enter__.return_value. \
write.assert_called_once_with('- cmd: ls\n when: 1430707243\n')
def test_and_(self, shell):
assert shell.and_('foo', 'bar') == 'foo; and bar'
def test_get_aliases(self, shell):
assert shell.get_aliases() == {'fish_config': 'fish_config',
'fuck': 'fuck',
'funced': 'funced',
'funcsave': 'funcsave',
'history': 'history',
'll': 'll',
'math': 'math',
'popd': 'popd',
'pushd': 'pushd',
'ruby': 'ruby'}
def test_app_alias(self, shell):
assert 'function fuck' in shell.app_alias('fuck')
assert 'function FUCK' in shell.app_alias('FUCK')
assert 'thefuck' in shell.app_alias('fuck')
assert 'TF_ALIAS' in shell.app_alias('fuck')
@pytest.mark.usefixtures('isfile')
class TestZsh(object):
@pytest.fixture
def shell(self):
return shells.Zsh()
@pytest.fixture(autouse=True)
def Popen(self, mocker):
mock = mocker.patch('thefuck.shells.Popen')
mock.return_value.stdout.read.return_value = (
b'fuck=\'eval $(thefuck $(fc -ln -1 | tail -n 1))\'\n'
b'l=\'ls -CF\'\n'
b'la=\'ls -A\'\n'
b'll=\'ls -alF\'')
return mock
@pytest.mark.parametrize('before, after', [
('fuck', 'eval $(thefuck $(fc -ln -1 | tail -n 1))'),
('pwd', 'pwd'),
('ll', 'ls -alF')])
def test_from_shell(self, before, after, shell):
assert shell.from_shell(before) == after
def test_to_shell(self, shell):
assert shell.to_shell('pwd') == 'pwd'
def test_put_to_history(self, builtins_open, mocker, shell):
mocker.patch('thefuck.shells.time',
return_value=1430707243.3517463)
shell.put_to_history('ls')
builtins_open.return_value.__enter__.return_value. \
write.assert_called_once_with(': 1430707243:0;ls\n')
def test_and_(self, shell):
assert shell.and_('ls', 'cd') == 'ls && cd'
def test_get_aliases(self, shell):
assert shell.get_aliases() == {
'fuck': 'eval $(thefuck $(fc -ln -1 | tail -n 1))',
'l': 'ls -CF',
'la': 'ls -A',
'll': 'ls -alF'}
def test_app_alias(self, shell):
assert 'alias fuck' in shell.app_alias('fuck')
assert 'alias FUCK' in shell.app_alias('FUCK')
assert 'thefuck' in shell.app_alias('fuck')
assert 'TF_ALIAS' in shell.app_alias('fuck')
def test_get_history(self, history_lines, shell):
history_lines([': 1432613911:0;ls', ': 1432613916:0;rm'])
assert list(shell.get_history()) == ['ls', 'rm']

View File

@@ -1,9 +1,12 @@
# -*- coding: utf-8 -*-
import os
from subprocess import PIPE
from mock import Mock
from pathlib import Path
import pytest
from tests.utils import CorrectedCommand, Rule, Command
from thefuck import conf
from thefuck import const
from thefuck.exceptions import EmptyCommand
@@ -19,6 +22,12 @@ class TestCorrectedCommand(object):
assert {CorrectedCommand('ls', None, 100),
CorrectedCommand('ls', None, 200)} == {CorrectedCommand('ls')}
def test_representable(self):
assert '{}'.format(CorrectedCommand('ls', None, 100)) == \
'CorrectedCommand(script=ls, side_effect=None, priority=100)'
assert u'{}'.format(CorrectedCommand(u'echo café', None, 100)) == \
u'CorrectedCommand(script=echo café, side_effect=None, priority=100)'
class TestRule(object):
def test_from_path(self, mocker):
@@ -31,19 +40,20 @@ class TestRule(object):
enabled_by_default=True,
priority=900,
requires_output=True))
assert Rule.from_path(Path('/rules/bash.py')) \
rule_path = os.path.join(os.sep, 'rules', 'bash.py')
assert Rule.from_path(Path(rule_path)) \
== Rule('bash', match, get_new_command, priority=900)
load_source.assert_called_once_with('bash', '/rules/bash.py')
load_source.assert_called_once_with('bash', rule_path)
@pytest.mark.parametrize('rules, exclude_rules, rule, is_enabled', [
(conf.DEFAULT_RULES, [], Rule('git', enabled_by_default=True), True),
(conf.DEFAULT_RULES, [], Rule('git', enabled_by_default=False), False),
(const.DEFAULT_RULES, [], Rule('git', enabled_by_default=True), True),
(const.DEFAULT_RULES, [], Rule('git', enabled_by_default=False), False),
([], [], Rule('git', enabled_by_default=False), False),
([], [], Rule('git', enabled_by_default=True), False),
(conf.DEFAULT_RULES + ['git'], [], Rule('git', enabled_by_default=False), True),
(const.DEFAULT_RULES + ['git'], [], Rule('git', enabled_by_default=False), True),
(['git'], [], Rule('git', enabled_by_default=False), True),
(conf.DEFAULT_RULES, ['git'], Rule('git', enabled_by_default=True), False),
(conf.DEFAULT_RULES, ['git'], Rule('git', enabled_by_default=False), False),
(const.DEFAULT_RULES, ['git'], Rule('git', enabled_by_default=True), False),
(const.DEFAULT_RULES, ['git'], Rule('git', enabled_by_default=False), False),
([], ['git'], Rule('git', enabled_by_default=True), False),
([], ['git'], Rule('git', enabled_by_default=False), False)])
def test_is_enabled(self, settings, rules, exclude_rules, rule, is_enabled):
@@ -95,11 +105,6 @@ class TestCommand(object):
monkeypatch.setattr('thefuck.types.Command._wait_output',
staticmethod(lambda *_: True))
@pytest.fixture(autouse=True)
def generic_shell(self, monkeypatch):
monkeypatch.setattr('thefuck.shells.from_shell', lambda x: x)
monkeypatch.setattr('thefuck.shells.to_shell', lambda x: x)
def test_from_script_calls(self, Popen, settings):
settings.env = {}
assert Command.from_raw_script(
@@ -107,6 +112,7 @@ class TestCommand(object):
'apt-get search vim', 'stdout', 'stderr')
Popen.assert_called_once_with('apt-get search vim',
shell=True,
stdin=PIPE,
stdout=PIPE,
stderr=PIPE,
env={})
@@ -122,4 +128,3 @@ class TestCommand(object):
else:
with pytest.raises(EmptyCommand):
Command.from_raw_script(script)

View File

@@ -4,39 +4,36 @@ import pytest
from itertools import islice
from thefuck import ui
from thefuck.types import CorrectedCommand
from thefuck import const
@pytest.fixture
def patch_getch(monkeypatch):
def patch_get_key(monkeypatch):
def patch(vals):
def getch():
for val in vals:
if val == KeyboardInterrupt:
raise val
else:
yield val
getch_gen = getch()
monkeypatch.setattr('thefuck.ui.getch', lambda: next(getch_gen))
vals = iter(vals)
monkeypatch.setattr('thefuck.ui.get_key', lambda: next(vals))
return patch
def test_read_actions(patch_getch):
patch_getch([ # Enter:
'\n',
# Enter:
'\r',
# Ignored:
'x', 'y',
# Up:
'\x1b', '[', 'A',
# Down:
'\x1b', '[', 'B',
# Ctrl+C:
KeyboardInterrupt], )
def test_read_actions(patch_get_key):
patch_get_key([
# Enter:
'\n',
# Enter:
'\r',
# Ignored:
'x', 'y',
# Up:
const.KEY_UP,
# Down:
const.KEY_DOWN,
# Ctrl+C:
const.KEY_CTRL_C])
assert list(islice(ui.read_actions(), 5)) \
== [ui.SELECT, ui.SELECT, ui.PREVIOUS, ui.NEXT, ui.ABORT]
== [const.ACTION_SELECT, const.ACTION_SELECT,
const.ACTION_PREVIOUS, const.ACTION_NEXT,
const.ACTION_ABORT]
def test_command_selector():
@@ -80,25 +77,25 @@ class TestSelectCommand(object):
== commands_with_side_effect[0]
assert capsys.readouterr() == ('', 'ls (+side effect)\n')
def test_with_confirmation(self, capsys, patch_getch, commands):
patch_getch(['\n'])
def test_with_confirmation(self, capsys, patch_get_key, commands):
patch_get_key(['\n'])
assert ui.select_command(iter(commands)) == commands[0]
assert capsys.readouterr() == ('', u'\x1b[1K\rls [enter/↑/↓/ctrl+c]\n')
def test_with_confirmation_abort(self, capsys, patch_getch, commands):
patch_getch([KeyboardInterrupt])
def test_with_confirmation_abort(self, capsys, patch_get_key, commands):
patch_get_key([const.KEY_CTRL_C])
assert ui.select_command(iter(commands)) is None
assert capsys.readouterr() == ('', u'\x1b[1K\rls [enter/↑/↓/ctrl+c]\nAborted\n')
def test_with_confirmation_with_side_effct(self, capsys, patch_getch,
def test_with_confirmation_with_side_effct(self, capsys, patch_get_key,
commands_with_side_effect):
patch_getch(['\n'])
assert ui.select_command(iter(commands_with_side_effect))\
patch_get_key(['\n'])
assert ui.select_command(iter(commands_with_side_effect)) \
== commands_with_side_effect[0]
assert capsys.readouterr() == ('', u'\x1b[1K\rls (+side effect) [enter/↑/↓/ctrl+c]\n')
def test_with_confirmation_select_second(self, capsys, patch_getch, commands):
patch_getch(['\x1b', '[', 'B', '\n'])
def test_with_confirmation_select_second(self, capsys, patch_get_key, commands):
patch_get_key([const.KEY_DOWN, '\n'])
assert ui.select_command(iter(commands)) == commands[1]
assert capsys.readouterr() == (
'', u'\x1b[1K\rls [enter/↑/↓/ctrl+c]\x1b[1K\rcd [enter/↑/↓/ctrl+c]\n')

View File

@@ -3,7 +3,8 @@ from mock import Mock
import six
from thefuck.utils import default_settings, \
memoize, get_closest, get_all_executables, replace_argument, \
get_all_matched_commands, is_app, for_app, cache, compatibility_call
get_all_matched_commands, is_app, for_app, cache, compatibility_call, \
get_valid_history_without_current
from tests.utils import Command
@@ -50,7 +51,7 @@ class TestGetClosest(object):
@pytest.fixture
def get_aliases(mocker):
mocker.patch('thefuck.shells.get_aliases',
mocker.patch('thefuck.shells.shell.get_aliases',
return_value=['vim', 'apt-get', 'fsck', 'fuck'])
@@ -162,10 +163,10 @@ class TestCache(object):
@pytest.fixture
def key(self):
if six.PY3:
return 'tests.test_utils.<function TestCache.fn.<locals>.fn '
else:
if six.PY2:
return 'tests.test_utils.<function fn '
else:
return 'tests.test_utils.<function TestCache.fn.<locals>.fn '
def test_with_blank_cache(self, shelve, fn, key):
assert shelve == {}
@@ -229,3 +230,29 @@ class TestCompatibilityCall(object):
return True
assert compatibility_call(side_effect, Command(), Command())
class TestGetValidHistoryWithoutCurrent(object):
@pytest.fixture(autouse=True)
def history(self, mocker):
return mocker.patch('thefuck.shells.shell.get_history',
return_value=['le cat', 'fuck', 'ls cat',
'diff x', 'nocommand x'])
@pytest.fixture(autouse=True)
def alias(self, mocker):
return mocker.patch('thefuck.utils.get_alias',
return_value='fuck')
@pytest.fixture(autouse=True)
def callables(self, mocker):
return mocker.patch('thefuck.utils.get_all_executables',
return_value=['diff', 'ls'])
@pytest.mark.parametrize('script, result', [
('le cat', ['ls cat', 'diff x']),
('diff x', ['ls cat']),
('fuck', ['ls cat', 'diff x'])])
def test_get_valid_history_without_current(self, script, result):
command = Command(script=script)
assert get_valid_history_without_current(command) == result

View File

@@ -1,5 +1,5 @@
from thefuck import types
from thefuck.conf import DEFAULT_PRIORITY
from thefuck.const import DEFAULT_PRIORITY
class Command(types.Command):

View File

@@ -3,40 +3,7 @@ import os
import sys
from pathlib import Path
from six import text_type
ALL_ENABLED = object()
DEFAULT_RULES = [ALL_ENABLED]
DEFAULT_PRIORITY = 1000
DEFAULT_SETTINGS = {'rules': DEFAULT_RULES,
'exclude_rules': [],
'wait_command': 3,
'require_confirmation': True,
'no_colors': False,
'debug': False,
'priority': {},
'env': {'LC_ALL': 'C', 'LANG': 'C', 'GIT_TRACE': '1'}}
ENV_TO_ATTR = {'THEFUCK_RULES': 'rules',
'THEFUCK_EXCLUDE_RULES': 'exclude_rules',
'THEFUCK_WAIT_COMMAND': 'wait_command',
'THEFUCK_REQUIRE_CONFIRMATION': 'require_confirmation',
'THEFUCK_NO_COLORS': 'no_colors',
'THEFUCK_PRIORITY': 'priority',
'THEFUCK_DEBUG': 'debug'}
SETTINGS_HEADER = u"""# ~/.thefuck/settings.py: The Fuck settings file
#
# The rules are defined as in the example bellow:
#
# rules = ['cd_parent', 'git_push', 'python_command', 'sudo']
#
# The default values are as follows. Uncomment and change to fit your needs.
# See https://github.com/nvbn/thefuck#settings for more information.
#
"""
from . import const
class Settings(dict):
@@ -67,13 +34,25 @@ class Settings(dict):
settings_path = self.user_dir.joinpath('settings.py')
if not settings_path.is_file():
with settings_path.open(mode='w') as settings_file:
settings_file.write(SETTINGS_HEADER)
for setting in DEFAULT_SETTINGS.items():
settings_file.write(const.SETTINGS_HEADER)
for setting in const.DEFAULT_SETTINGS.items():
settings_file.write(u'# {} = {}\n'.format(*setting))
def _get_user_dir_path(self):
# for backward compatibility, use `~/.thefuck` if it exists
legacy_user_dir = Path(os.path.expanduser('~/.thefuck'))
if legacy_user_dir.is_dir():
return legacy_user_dir
else:
default_xdg_config_dir = os.path.expanduser("~/.config")
xdg_config_dir = os.getenv("XDG_CONFIG_HOME", default_xdg_config_dir)
return Path(os.path.join(xdg_config_dir, 'thefuck'))
def _setup_user_dir(self):
"""Returns user config dir, create it when it doesn't exist."""
user_dir = Path(os.path.expanduser('~/.thefuck'))
user_dir = self._get_user_dir_path()
rules_dir = user_dir.joinpath('rules')
if not rules_dir.is_dir():
rules_dir.mkdir(parents=True)
@@ -84,14 +63,14 @@ class Settings(dict):
settings = load_source(
'settings', text_type(self.user_dir.joinpath('settings.py')))
return {key: getattr(settings, key)
for key in DEFAULT_SETTINGS.keys()
for key in const.DEFAULT_SETTINGS.keys()
if hasattr(settings, key)}
def _rules_from_env(self, val):
"""Transforms rules list from env-string to python."""
val = val.split(':')
if 'DEFAULT_RULES' in val:
val = DEFAULT_RULES + [rule for rule in val if rule != 'DEFAULT_RULES']
val = const.DEFAULT_RULES + [rule for rule in val if rule != 'DEFAULT_RULES']
return val
def _priority_from_env(self, val):
@@ -112,16 +91,19 @@ class Settings(dict):
return dict(self._priority_from_env(val))
elif attr == 'wait_command':
return int(val)
elif attr in ('require_confirmation', 'no_colors', 'debug'):
elif attr in ('require_confirmation', 'no_colors', 'debug',
'alter_history'):
return val.lower() == 'true'
elif attr == 'history_limit':
return int(val)
else:
return val
def _settings_from_env(self):
"""Loads settings from env."""
return {attr: self._val_from_env(env, attr)
for env, attr in ENV_TO_ATTR.items()
for env, attr in const.ENV_TO_ATTR.items()
if env in os.environ}
settings = Settings(DEFAULT_SETTINGS)
settings = Settings(const.DEFAULT_SETTINGS)

56
thefuck/const.py Normal file
View File

@@ -0,0 +1,56 @@
# -*- encoding: utf-8 -*-
class _GenConst(object):
def __init__(self, name):
self._name = name
def __repr__(self):
return u'<const: {}>'.format(self._name)
KEY_UP = _GenConst('')
KEY_DOWN = _GenConst('')
KEY_CTRL_C = _GenConst('Ctrl+C')
ACTION_SELECT = _GenConst('select')
ACTION_ABORT = _GenConst('abort')
ACTION_PREVIOUS = _GenConst('previous')
ACTION_NEXT = _GenConst('next')
ALL_ENABLED = _GenConst('All rules enabled')
DEFAULT_RULES = [ALL_ENABLED]
DEFAULT_PRIORITY = 1000
DEFAULT_SETTINGS = {'rules': DEFAULT_RULES,
'exclude_rules': [],
'wait_command': 3,
'require_confirmation': True,
'no_colors': False,
'debug': False,
'priority': {},
'history_limit': None,
'alter_history': True,
'env': {'LC_ALL': 'C', 'LANG': 'C', 'GIT_TRACE': '1'}}
ENV_TO_ATTR = {'THEFUCK_RULES': 'rules',
'THEFUCK_EXCLUDE_RULES': 'exclude_rules',
'THEFUCK_WAIT_COMMAND': 'wait_command',
'THEFUCK_REQUIRE_CONFIRMATION': 'require_confirmation',
'THEFUCK_NO_COLORS': 'no_colors',
'THEFUCK_DEBUG': 'debug',
'THEFUCK_PRIORITY': 'priority',
'THEFUCK_HISTORY_LIMIT': 'history_limit',
'THEFUCK_ALTER_HISTORY': 'alter_history'}
SETTINGS_HEADER = u"""# The Fuck settings file
#
# The rules are defined as in the example bellow:
#
# rules = ['cd_parent', 'git_push', 'python_command', 'sudo']
#
# The default values are as follows. Uncomment and change to fit your needs.
# See https://github.com/nvbn/thefuck#settings for more information.
#
"""

View File

@@ -55,7 +55,7 @@ def organize_commands(corrected_commands):
key=lambda corrected_command: corrected_command.priority)
logs.debug('Corrected commands: '.format(
', '.join(str(cmd) for cmd in [first_command] + sorted_commands)))
', '.join(u'{}'.format(cmd) for cmd in [first_command] + sorted_commands)))
for command in sorted_commands:
yield command

View File

@@ -28,29 +28,29 @@ def exception(title, exc_info):
def rule_failed(rule, exc_info):
exception('Rule {}'.format(rule.name), exc_info)
exception(u'Rule {}'.format(rule.name), exc_info)
def failed(msg):
sys.stderr.write('{red}{msg}{reset}\n'.format(
sys.stderr.write(u'{red}{msg}{reset}\n'.format(
msg=msg,
red=color(colorama.Fore.RED),
reset=color(colorama.Style.RESET_ALL)))
def show_corrected_command(corrected_command):
sys.stderr.write('{bold}{script}{reset}{side_effect}\n'.format(
sys.stderr.write(u'{bold}{script}{reset}{side_effect}\n'.format(
script=corrected_command.script,
side_effect=' (+side effect)' if corrected_command.side_effect else '',
side_effect=u' (+side effect)' if corrected_command.side_effect else u'',
bold=color(colorama.Style.BRIGHT),
reset=color(colorama.Style.RESET_ALL)))
def confirm_text(corrected_command):
sys.stderr.write(
('{clear}{bold}{script}{reset}{side_effect} '
'[{green}enter{reset}/{blue}{reset}/{blue}{reset}'
'/{red}ctrl+c{reset}]').format(
(u'{clear}{bold}{script}{reset}{side_effect} '
u'[{green}enter{reset}/{blue}{reset}/{blue}{reset}'
u'/{red}ctrl+c{reset}]').format(
script=corrected_command.script,
side_effect=' (+side effect)' if corrected_command.side_effect else '',
clear='\033[1K\r',

View File

@@ -1,19 +1,23 @@
# Initialize output before importing any module, that can use colorama.
from .system import init_output
init_output()
from argparse import ArgumentParser
from warnings import warn
from pprint import pformat
import sys
import colorama
from . import logs, types, shells
from . import logs, types
from .shells import shell
from .conf import settings
from .corrector import get_corrected_commands
from .exceptions import EmptyCommand
from .utils import get_installation_info
from .utils import get_installation_info, get_alias
from .ui import select_command
def fix_command():
"""Fixes previous command. Used when `thefuck` called without arguments."""
colorama.init()
settings.init()
with logs.debug_time('Total'):
logs.debug(u'Run with settings: {}'.format(pformat(settings)))
@@ -39,10 +43,10 @@ def print_alias(entry_point=True):
else:
position = 2
alias = shells.thefuck_alias()
alias = get_alias()
if len(sys.argv) > position:
alias = sys.argv[position]
print(shells.app_alias(alias))
print(shell.app_alias(alias))
def how_to_configure_alias():
@@ -51,19 +55,18 @@ def how_to_configure_alias():
It'll be only visible when user type fuck and when alias isn't configured.
"""
colorama.init()
settings.init()
logs.how_to_configure_alias(shells.how_to_configure())
logs.how_to_configure_alias(shell.how_to_configure())
def main():
parser = ArgumentParser(prog='thefuck')
version = get_installation_info().version
parser.add_argument(
'-v', '--version',
action='version',
version='The Fuck {} using Python {}'.format(
version, sys.version.split()[0]))
'-v', '--version',
action='version',
version='The Fuck {} using Python {}'.format(
version, sys.version.split()[0]))
parser.add_argument('-a', '--alias',
action='store_true',
help='[custom-alias-name] prints alias for current shell')

View File

@@ -1,8 +1,10 @@
from thefuck import shells
from thefuck.specific.apt import apt_available
from thefuck.utils import memoize
from thefuck.shells import shell
try:
import CommandNotFound
enabled_by_default = apt_available
except ImportError:
enabled_by_default = False
@@ -26,5 +28,5 @@ def match(command):
def get_new_command(command):
name = get_package(command.script)
formatme = shells.and_('sudo apt-get install {}', '{}')
formatme = shell.and_('sudo apt-get install {}', '{}')
return formatme.format(name, command.script)

View File

@@ -1,6 +1,9 @@
import re
from thefuck.specific.apt import apt_available
from thefuck.utils import for_app
enabled_by_default = apt_available
@for_app('apt-get')
def match(command):

View File

@@ -0,0 +1,56 @@
import subprocess
from thefuck.specific.apt import apt_available
from thefuck.specific.sudo import sudo_support
from thefuck.utils import for_app, eager, replace_command
enabled_by_default = apt_available
@for_app('apt', 'apt-get', 'apt-cache')
@sudo_support
def match(command):
return 'E: Invalid operation' in command.stderr
@eager
def _parse_apt_operations(help_text_lines):
is_commands_list = False
for line in help_text_lines:
line = line.decode().strip()
if is_commands_list and line:
yield line.split()[0]
elif line.startswith('Basic commands:'):
is_commands_list = True
@eager
def _parse_apt_get_and_cache_operations(help_text_lines):
is_commands_list = False
for line in help_text_lines:
line = line.decode().strip()
if is_commands_list:
if not line:
return
yield line.split()[0]
elif line.startswith('Commands:'):
is_commands_list = True
def _get_operations(app):
proc = subprocess.Popen([app, '--help'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
lines = proc.stdout.readlines()
if app == 'apt':
return _parse_apt_operations(lines)
else:
return _parse_apt_get_and_cache_operations(lines)
@sudo_support
def get_new_command(command):
invalid_operation = command.stderr.split()[-1]
operations = _get_operations(command.script_parts[0])
return replace_command(command, invalid_operation, operations)

View File

@@ -1,10 +1,9 @@
import os
import re
from thefuck.utils import get_closest, replace_argument, which
from thefuck.specific.brew import get_brew_path_prefix
from thefuck.utils import get_closest, replace_argument
from thefuck.specific.brew import get_brew_path_prefix, brew_available
enabled_by_default = bool(which('brew'))
enabled_by_default = brew_available
def _get_formulas():

View File

@@ -1,12 +1,14 @@
import os
import re
from thefuck.utils import get_closest, replace_command
from thefuck.specific.brew import get_brew_path_prefix
from thefuck.specific.brew import get_brew_path_prefix, brew_available
BREW_CMD_PATH = '/Library/Homebrew/cmd'
TAP_PATH = '/Library/Taps'
TAP_CMD_PATH = '/%s/%s/cmd'
enabled_by_default = brew_available
def _get_brew_commands(brew_path_prefix):
"""To get brew default commands on local environment"""
@@ -53,7 +55,7 @@ def _brew_commands():
if brew_path_prefix:
try:
return _get_brew_commands(brew_path_prefix) \
+ _get_brew_tap_specific_commands(brew_path_prefix)
+ _get_brew_tap_specific_commands(brew_path_prefix)
except OSError:
pass

View File

@@ -4,6 +4,9 @@
# > brew upgrade
# Warning: brew upgrade with no arguments will change behaviour soon!
# It currently upgrades all formula but this will soon change to require '--all'.
from thefuck.specific.brew import brew_available
enabled_by_default = brew_available
def match(command):

View File

@@ -2,14 +2,14 @@ import re
from thefuck.utils import replace_argument, for_app
@for_app('cargo')
@for_app('cargo', at_least=1)
def match(command):
return ('No such subcommand' in command.stderr
and 'Did you mean' in command.stderr)
def get_new_command(command):
broken = command.script.split()[1]
broken = command.script_parts[1]
fix = re.findall(r'Did you mean `([^`]*)`', command.stderr)[0]
return replace_argument(command.script, broken, fix)

View File

@@ -1,6 +1,7 @@
"""Attempts to spellcheck and correct failed cd commands"""
import os
import six
from difflib import get_close_matches
from thefuck.specific.sudo import sudo_support
from thefuck.rules import cd_mkdir
@@ -33,10 +34,13 @@ def get_new_command(command):
defaults to the rules of cd_mkdir.
Change sensitivity by changing MAX_ALLOWED_DIFF. Default value is 0.6
"""
dest = command.script.split()[1].split(os.sep)
dest = command.script_parts[1].split(os.sep)
if dest[-1] == '':
dest = dest[:-1]
cwd = os.getcwd()
if six.PY2:
cwd = os.getcwdu()
else:
cwd = os.getcwd()
for directory in dest:
if directory == ".":
continue
@@ -48,7 +52,7 @@ def get_new_command(command):
cwd = os.path.join(cwd, best_matches[0])
else:
return cd_mkdir.get_new_command(command)
return 'cd "{0}"'.format(cwd)
return u'cd "{0}"'.format(cwd)
enabled_by_default = True

View File

@@ -1,17 +1,18 @@
import re
from thefuck import shells
from thefuck.utils import for_app
from thefuck.specific.sudo import sudo_support
from thefuck.shells import shell
@sudo_support
@for_app('cd')
def match(command):
return (('no such file or directory' in command.stderr.lower()
or 'cd: can\'t cd to' in command.stderr.lower()))
or 'cd: can\'t cd to' in command.stderr.lower()
or 'the system cannot find the path specified.' in command.stderr.lower()))
@sudo_support
def get_new_command(command):
repl = shells.and_('mkdir -p \\1', 'cd \\1')
repl = shell.and_('mkdir -p \\1', 'cd \\1')
return re.sub(r'^cd (.*)', repl, command.script)

View File

@@ -1,7 +1,7 @@
from thefuck.utils import for_app
@for_app(['g++', 'clang++'])
@for_app('g++', 'clang++')
def match(command):
return ('This file requires compiler and library support for the '
'ISO C++ 2011 standard.' in command.stderr or

View File

@@ -1,7 +1,12 @@
import tarfile
import os
from thefuck import shells
from thefuck.utils import for_app
from thefuck.shells import shell
tar_extensions = ('.tar', '.tar.Z', '.tar.bz2', '.tar.gz', '.tar.lz',
'.tar.lzma', '.tar.xz', '.taz', '.tb2', '.tbz', '.tbz2',
'.tgz', '.tlz', '.txz', '.tz')
def _is_tar_extract(cmd):
@@ -14,11 +19,7 @@ def _is_tar_extract(cmd):
def _tar_file(cmd):
tar_extensions = ('.tar', '.tar.Z', '.tar.bz2', '.tar.gz', '.tar.lz',
'.tar.lzma', '.tar.xz', '.taz', '.tb2', '.tbz', '.tbz2',
'.tgz', '.tlz', '.txz', '.tz')
for c in cmd.split():
for c in cmd:
for ext in tar_extensions:
if c.endswith(ext):
return (c, c[0:len(c) - len(ext)])
@@ -28,16 +29,17 @@ def _tar_file(cmd):
def match(command):
return ('-C' not in command.script
and _is_tar_extract(command.script)
and _tar_file(command.script) is not None)
and _tar_file(command.script_parts) is not None)
def get_new_command(command):
return shells.and_('mkdir -p {dir}', '{cmd} -C {dir}') \
.format(dir=_tar_file(command.script)[1], cmd=command.script)
dir = shell.quote(_tar_file(command.script_parts)[1])
return shell.and_('mkdir -p {dir}', '{cmd} -C {dir}') \
.format(dir=dir, cmd=command.script)
def side_effect(old_cmd, command):
with tarfile.TarFile(_tar_file(old_cmd.script)[0]) as archive:
with tarfile.TarFile(_tar_file(old_cmd.script_parts)[0]) as archive:
for file in archive.getnames():
try:
os.remove(file)

View File

@@ -1,11 +1,15 @@
import os
import zipfile
from thefuck.utils import for_app
from thefuck.shells import shell
def _is_bad_zip(file):
with zipfile.ZipFile(file, 'r') as archive:
return len(archive.namelist()) > 1
try:
with zipfile.ZipFile(file, 'r') as archive:
return len(archive.namelist()) > 1
except:
return False
def _zip_file(command):
@@ -13,22 +17,29 @@ def _zip_file(command):
# unzip [-flags] file[.zip] [file(s) ...] [-x file(s) ...]
# ^ ^ files to unzip from the archive
# archive to unzip
for c in command.script.split()[1:]:
for c in command.script_parts[1:]:
if not c.startswith('-'):
if c.endswith('.zip'):
return c
else:
return '{}.zip'.format(c)
return u'{}.zip'.format(c)
@for_app('unzip')
def match(command):
return ('-d' not in command.script
and _is_bad_zip(_zip_file(command)))
if '-d' in command.script:
return False
zip_file = _zip_file(command)
if zip_file:
return _is_bad_zip(zip_file)
else:
return False
def get_new_command(command):
return '{} -d {}'.format(command.script, _zip_file(command)[:-4])
return u'{} -d {}'.format(
command.script, shell.quote(_zip_file(command)[:-4]))
def side_effect(old_cmd, command):

View File

@@ -1,11 +1,13 @@
def match(command):
split_command = command.script.split()
split_command = command.script_parts
return len(split_command) >= 2 and split_command[0] == split_command[1]
return (split_command
and len(split_command) >= 2
and split_command[0] == split_command[1])
def get_new_command(command):
return command.script[command.script.find(' ')+1:]
return ' '.join(command.script_parts[1:])
# it should be rare enough to actually have to type twice the same word, so
# this rule can have a higher priority to come before things like "cd cd foo"

View File

@@ -2,43 +2,43 @@ import re
import os
from thefuck.utils import memoize, default_settings
from thefuck.conf import settings
from thefuck import shells
from thefuck.shells import shell
# order is important: only the first match is considered
patterns = (
# js, node:
'^ at {file}:{line}:{col}',
# cargo:
'^ {file}:{line}:{col}',
# python, thefuck:
'^ File "{file}", line {line}',
# awk:
'^awk: {file}:{line}:',
# git
'^fatal: bad config file line {line} in {file}',
# llc:
'^llc: {file}:{line}:{col}:',
# lua:
'^lua: {file}:{line}:',
# fish:
'^{file} \\(line {line}\\):',
# bash, sh, ssh:
'^{file}: line {line}: ',
# cargo, clang, gcc, go, pep8, rustc:
'^{file}:{line}:{col}',
# ghc, make, ruby, zsh:
'^{file}:{line}:',
# perl:
'at {file} line {line}',
)
# js, node:
'^ at {file}:{line}:{col}',
# cargo:
'^ {file}:{line}:{col}',
# python, thefuck:
'^ File "{file}", line {line}',
# awk:
'^awk: {file}:{line}:',
# git
'^fatal: bad config file line {line} in {file}',
# llc:
'^llc: {file}:{line}:{col}:',
# lua:
'^lua: {file}:{line}:',
# fish:
'^{file} \\(line {line}\\):',
# bash, sh, ssh:
'^{file}: line {line}: ',
# cargo, clang, gcc, go, pep8, rustc:
'^{file}:{line}:{col}',
# ghc, make, ruby, zsh:
'^{file}:{line}:',
# perl:
'at {file} line {line}',
)
# for the sake of readability do not use named groups above
def _make_pattern(pattern):
pattern = pattern.replace('{file}', '(?P<file>[^:\n]+)') \
.replace('{line}', '(?P<line>[0-9]+)') \
.replace('{col}', '(?P<col>[0-9]+)')
.replace('{col}', '(?P<col>[0-9]+)')
return re.compile(pattern, re.MULTILINE)
patterns = [_make_pattern(p).search for p in patterns]
@@ -58,7 +58,7 @@ def match(command):
return _search(command.stderr) or _search(command.stdout)
@default_settings({'fixlinecmd': '{editor} {file} +{line}',
@default_settings({'fixlinecmd': u'{editor} {file} +{line}',
'fixcolcmd': None})
def get_new_command(command):
m = _search(command.stderr) or _search(command.stdout)
@@ -75,4 +75,4 @@ def get_new_command(command):
file=m.group('file'),
line=m.group('line'))
return shells.and_(editor_call, command.script)
return shell.and_(editor_call, command.script)

View File

@@ -1,19 +1,18 @@
import re
from thefuck import shells
from thefuck.shells import shell
from thefuck.specific.git import git_support
@git_support
def match(command):
return ('did not match any file(s) known to git.' in command.stderr
and "Did you forget to 'git add'?" in command.stderr)
return 'did not match any file(s) known to git.' in command.stderr
@git_support
def get_new_command(command):
missing_file = re.findall(
r"error: pathspec '([^']*)' "
r"did not match any file\(s\) known to git.", command.stderr)[0]
r"error: pathspec '([^']*)' "
r'did not match any file\(s\) known to git.', command.stderr)[0]
formatme = shells.and_('git add -- {}', '{}')
formatme = shell.and_('git add -- {}', '{}')
return formatme.format(missing_file, command.script)

View File

@@ -0,0 +1,23 @@
import re
from thefuck.shells import shell
from thefuck.specific.git import git_support
from thefuck.utils import eager
@git_support
def match(command):
return ('branch' in command.script
and "fatal: A branch named '" in command.stderr
and " already exists." in command.stderr)
@git_support
@eager
def get_new_command(command):
branch_name = re.findall(
r"fatal: A branch named '([^']*)' already exists.", command.stderr)[0]
new_command_templates = [['git branch -d {0}', 'git branch {0}'],
['git branch -D {0}', 'git branch {0}'],
['git checkout {0}']]
for new_command_template in new_command_templates:
yield shell.and_(*new_command_template).format(branch_name)

View File

@@ -1,13 +1,14 @@
from thefuck import shells
from thefuck.shells import shell
from thefuck.specific.git import git_support
@git_support
def match(command):
# catches "git branch list" in place of "git branch"
return command.script.split()[1:] == 'branch list'.split()
return (command.script_parts
and command.script_parts[1:] == 'branch list'.split())
@git_support
def get_new_command(command):
return shells.and_('git branch --delete list', 'git branch')
return shell.and_('git branch --delete list', 'git branch')

View File

@@ -1,8 +1,9 @@
import re
import subprocess
from thefuck import shells, utils
from thefuck import utils
from thefuck.utils import replace_argument
from thefuck.specific.git import git_support
from thefuck.shells import shell
@git_support
@@ -34,5 +35,5 @@ def get_new_command(command):
if closest_branch:
return replace_argument(command.script, missing_file, closest_branch)
else:
return shells.and_('git branch {}', '{}').format(
return shell.and_('git branch {}', '{}').format(
missing_file, command.script)

View File

@@ -5,9 +5,8 @@ from thefuck.specific.git import git_support
@git_support
def match(command):
splited_script = command.script.split()
if len(splited_script) > 1:
return (splited_script[1] == 'stash'
if command.script_parts and len(command.script_parts) > 1:
return (command.script_parts[1] == 'stash'
and 'usage:' in command.stderr)
else:
return False
@@ -26,12 +25,12 @@ stash_commands = (
@git_support
def get_new_command(command):
stash_cmd = command.script.split()[2]
stash_cmd = command.script_parts[2]
fixed = utils.get_closest(stash_cmd, stash_commands, fallback_to_first=False)
if fixed is not None:
return replace_argument(command.script, stash_cmd, fixed)
else:
cmd = command.script.split()
cmd = command.script_parts[:]
cmd.insert(2, 'save')
return ' '.join(cmd)

View File

@@ -0,0 +1,12 @@
from thefuck.specific.git import git_support
@git_support
def match(command):
return 'help' in command.script and ' is aliased to ' in command.stdout
@git_support
def get_new_command(command):
aliased = command.stdout.split('`', 2)[2].split("'", 1)[0].split(' ', 1)[0]
return 'git help {}'.format(aliased)

View File

@@ -1,4 +1,4 @@
from thefuck import shells
from thefuck.shells import shell
from thefuck.specific.git import git_support
@@ -14,4 +14,4 @@ def get_new_command(command):
branch = line.split(' ')[-1]
set_upstream = line.replace('<remote>', 'origin')\
.replace('<branch>', branch)
return shells.and_(set_upstream, command.script)
return shell.and_(set_upstream, command.script)

View File

@@ -12,7 +12,7 @@ def match(command):
@git_support
def get_new_command(command):
return replace_argument(command.script, 'push', 'push --force')
return replace_argument(command.script, 'push', 'push --force-with-lease')
enabled_by_default = False

View File

@@ -1,4 +1,4 @@
from thefuck import shells
from thefuck.shells import shell
from thefuck.utils import replace_argument
from thefuck.specific.git import git_support
@@ -13,5 +13,5 @@ def match(command):
@git_support
def get_new_command(command):
return shells.and_(replace_argument(command.script, 'push', 'pull'),
command.script)
return shell.and_(replace_argument(command.script, 'push', 'pull'),
command.script)

View File

@@ -0,0 +1,14 @@
from thefuck.utils import replace_argument
from thefuck.specific.git import git_support
@git_support
def match(command):
return ('set-url' in command.script and 'fatal: No such remote' in command.stderr)
def get_new_command(command):
return replace_argument(command.script, 'set-url', 'add')
enabled_by_default = True

View File

@@ -0,0 +1,15 @@
from thefuck.specific.git import git_support
@git_support
def match(command):
return (' rm ' in command.script
and "fatal: not removing '" in command.stderr
and "' recursively without -r" in command.stderr)
@git_support
def get_new_command(command):
index = command.script_parts.index('rm') + 1
command.script_parts.insert(index, '-r')
return u' '.join(command.script_parts)

View File

@@ -1,4 +1,4 @@
from thefuck import shells
from thefuck.shells import shell
from thefuck.specific.git import git_support
@@ -11,5 +11,5 @@ def match(command):
@git_support
def get_new_command(command):
formatme = shells.and_('git stash', '{}')
formatme = shell.and_('git stash', '{}')
return formatme.format(command.script)

View File

@@ -0,0 +1,14 @@
from thefuck.utils import replace_argument
from thefuck.specific.git import git_support
@git_support
def match(command):
return ('error: did you mean `' in command.stderr
and '` (with two dashes ?)' in command.stderr)
@git_support
def get_new_command(command):
to = command.stderr.split('`')[1]
return replace_argument(command.script, to[1:], to)

View File

@@ -0,0 +1,23 @@
import os
from thefuck.utils import for_app
def _get_actual_file(parts):
for part in parts[1:]:
if os.path.isfile(part) or os.path.isdir(part):
return part
@for_app('grep', 'egrep')
def match(command):
return ': No such file or directory' in command.stderr \
and _get_actual_file(command.script_parts)
def get_new_command(command):
actual_file = _get_actual_file(command.script_parts)
parts = command.script_parts[::]
# Moves file to the end of the script:
parts.remove(actual_file)
parts.append(actual_file)
return ' '.join(parts)

View File

@@ -7,4 +7,4 @@ def match(command):
def get_new_command(command):
return 'grep -r {}'.format(command.script[5:])
return u'grep -r {}'.format(command.script[5:])

View File

@@ -4,7 +4,7 @@ from thefuck.specific.sudo import sudo_support
@sudo_support
def match(command):
return os.path.exists(command.script.split()[0]) \
return command.script_parts and os.path.exists(command.script_parts[0]) \
and 'command not found' in command.stderr

View File

@@ -1,37 +1,15 @@
from difflib import get_close_matches
from thefuck.shells import get_history, thefuck_alias
from thefuck.utils import get_closest, memoize, get_all_executables
def _not_corrected(history, tf_alias):
"""Returns all lines from history except that comes before `fuck`."""
previous = None
for line in history:
if previous is not None and line != tf_alias:
yield previous
previous = line
if history:
yield history[-1]
@memoize
def _history_of_exists_without_current(command):
history = get_history()
tf_alias = thefuck_alias()
executables = get_all_executables()
return [line for line in _not_corrected(history, tf_alias)
if not line.startswith(tf_alias) and not line == command.script
and line.split(' ')[0] in executables]
from thefuck.utils import get_closest, get_valid_history_without_current
def match(command):
return len(get_close_matches(command.script,
_history_of_exists_without_current(command)))
get_valid_history_without_current(command)))
def get_new_command(command):
return get_closest(command.script,
_history_of_exists_without_current(command))
get_valid_history_without_current(command))
priority = 9999

View File

@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
"""Suggest creating symbolic link if hard link is not allowed.
Example:
> ln barDir barLink
ln: barDir: hard link not allowed for directory
--> ln -s barDir barLink
"""
import re
from thefuck.specific.sudo import sudo_support
@sudo_support
def match(command):
return (command.stderr.endswith("hard link not allowed for directory") and
command.script.startswith("ln "))
@sudo_support
def get_new_command(command):
return re.sub(r'^ln ', 'ln -s ', command.script)

View File

@@ -3,10 +3,10 @@ from thefuck.utils import for_app
@for_app('ls')
def match(command):
return 'ls -' not in command.script
return command.script_parts and 'ls -' not in command.script
def get_new_command(command):
command = command.script.split(' ')
command = command.script_parts[:]
command[0] = 'ls -lah'
return ' '.join(command)

View File

@@ -1,5 +1,9 @@
from thefuck.utils import for_app
@for_app('man', at_least=1)
def match(command):
return command.script.strip().startswith('man ')
return True
def get_new_command(command):
@@ -8,7 +12,7 @@ def get_new_command(command):
if '2' in command.script:
return command.script.replace("2", "3")
split_cmd2 = command.script.split()
split_cmd2 = command.script_parts
split_cmd3 = split_cmd2[:]
split_cmd2.insert(1, ' 2 ')

Some files were not shown because too many files have changed in this diff Show More