nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: abs-lang/abs
Pull request #224 (2019-05-14 13:00:04)

Fixing hash to JSON conversion
`"` were not escaped, thus would lead
to invalid json (`{"x": "\"y"}` would become `{"x": ""y"}`)


Patch:
diff --git a/object/object.go b/object/object.go
index 2e30506..5dcf8d6 100644
--- a/object/object.go
+++ b/object/object.go
@@ -184,7 +184,7 @@ type String struct {
 
 func (s *String) Type() ObjectType  { return STRING_OBJ }
 func (s *String) Inspect() string   { return s.Value }
-func (s *String) Json() string      { return `"` + s.Inspect() + `"` }
+func (s *String) Json() string      { return `"` + strings.ReplaceAll(s.Inspect(), `"`, `\"`) + `"` }
 func (s *String) ZeroValue() string { return "" }
 func (s *String) HashKey() HashKey {
 	return HashKey{Type: s.Type(), Value: s.Value}


Test patch:
diff --git a/evaluator/evaluator_test.go b/evaluator/evaluator_test.go
index ee63a0b..34fc832 100644
--- a/evaluator/evaluator_test.go
+++ b/evaluator/evaluator_test.go
@@ -1546,7 +1546,7 @@ func TestHashFunctions(t *testing.T) {
 		expected string
 	}{
 		{`
-		h = {"a": 1, "b": 2, "c": {"x": 10, "y":20}}
+		h = {"a": 1, "b": 2, "c": {"x": 10, "y":20}, "e": "\"test"}
 		hk = h.keys()
 		hk = keys(h)
 		hv = h.values()
@@ -1557,7 +1557,7 @@ func TestHashFunctions(t *testing.T) {
 		hp = pop(h, "c")
 		hp = h.pop("d")
 		str(h)
-		`, `{"b": 2}`,
+		`, `{"b": 2, "e": "\"test"}`,
 		},
 	}
Repository: abs-lang/abs
Pull request #270 (2019-08-19 06:34:51)

Errors with index ranges
ABS:

```
⧐  [0,1,2,3,4,5][2:3]
[2]
⧐  [0,1,2,3,4,5][2:4]
[2, 3]
⧐  [0,1,2,3,4,5][2:5]
[2, 3, 4, 5]
```

Python:

```
>>> [0,1,2,3,4,5][2:3]
[2]
>>> [0,1,2,3,4,5][2:4]
[2, 3]
>>> [0,1,2,3,4,5][2:5]
[2, 3, 4]
```


Patch:
diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go
index 5eb6821..fe04667 100644
--- a/evaluator/evaluator.go
+++ b/evaluator/evaluator.go
@@ -1092,11 +1092,16 @@ func evalIndexExpression(node *ast.IndexExpression, env *object.Environment) obj
 }
 
 func evalStringIndexExpression(tok token.Token, array, index object.Object, end object.Object, isRange bool) object.Object {
+	// TODO this gotta be refactored so that
+	// evalStringIndexExpression and evalArrayIndexExpression
+	// reuse most of their code, now it's a bit messy as
+	// the code is duplicated in both places
 	stringObject := array.(*object.String)
 	idx := index.(*object.Number).Int()
-	max := len(stringObject.Value)
+	max := len(stringObject.Value) - 1
 
 	if isRange {
+		max += 1
 		// A range's minimum value is 0
 		if idx < 0 {
 			idx = 0
@@ -1140,6 +1145,7 @@ func evalArrayIndexExpression(tok token.Token, array, index object.Object, end o
 	max := len(arrayObject.Elements) - 1
 
 	if isRange {
+		max += 1
 		// A range's minimum value is 0
 		if idx < 0 {
 			idx = 0
@@ -1153,7 +1159,7 @@ func evalArrayIndexExpression(tok token.Token, array, index object.Object, end o
 			if endIdx.Int() < 0 {
 				max = int(math.Max(float64(max+endIdx.Int()), 0))
 			} else if endIdx.Int() < max {
-				max = endIdx.Int() - 1
+				max = endIdx.Int()
 			}
 		} else if end != NULL {
 			// if the end index is not a number nor null, then we have an error
@@ -1167,7 +1173,7 @@ func evalArrayIndexExpression(tok token.Token, array, index object.Object, end o
 			return &object.Array{Token: tok, Elements: []object.Object{}}
 		}
 
-		return &object.Array{Token: tok, Elements: arrayObject.Elements[idx : max+1]}
+		return &object.Array{Token: tok, Elements: arrayObject.Elements[idx:max]}
 	}
 
 	if idx < 0 || idx > max {


Test patch:
diff --git a/evaluator/evaluator_test.go b/evaluator/evaluator_test.go
index be3fc8b..7ce1063 100644
--- a/evaluator/evaluator_test.go
+++ b/evaluator/evaluator_test.go
@@ -1390,6 +1390,10 @@ func TestArrayIndexExpressions(t *testing.T) {
 			"a = [1, 2, 3, 4, 5, 6, 7, 8, 9][-10:]; a[0]",
 			1,
 		},
+		{
+			`a = [0,1,2,3,4,5][2:5]; len(a)`,
+			3,
+		},
 	}
 
 	for _, tt := range tests {
@@ -1532,6 +1536,18 @@ func TestStringIndexExpressions(t *testing.T) {
 			`"123"[-10:{}]`,
 			`index ranges can only be numerical: got "{}" (type HASH)`,
 		},
+		{
+			`"123"[-2]`,
+			"",
+		},
+		{
+			`"123"[3]`,
+			"",
+		},
+		{
+			`"123"[0]`,
+			"1",
+		},
 	}
 
 	for _, tt := range tests {
Repository: abs-lang/abs
Pull request #323 (2020-02-15 12:06:00)

Ability to call a function with dynamic arguments: fn.call(args)
`call` calls a function with the arguments passed:

``` bash
multiplier = f(x, y) { x * y }
multiplier.call([10, 2]) # 20
```


Patch:
diff --git a/docs/types/function.md b/docs/types/function.md
index 73f4eef..f96b12f 100644
--- a/docs/types/function.md
+++ b/docs/types/function.md
@@ -93,6 +93,15 @@ f(x){}.str()
 # }
 ```
 
+### call(args)
+
+Calls a function with the given arguments:
+
+``` bash
+doubler = f(x) { x * 2 }
+doubler.call([10]) # 20
+```
+
 ## Next
 
 That's about it for this section!
diff --git a/evaluator/functions.go b/evaluator/functions.go
index 1fe03ac..436f760 100644
--- a/evaluator/functions.go
+++ b/evaluator/functions.go
@@ -136,6 +136,11 @@ func getFns() map[string]*object.Builtin {
 			Types: []string{},
 			Fn:    typeFn,
 		},
+		// fn.call(args_array)
+		"call": &object.Builtin{
+			Types: []string{object.FUNCTION_OBJ, object.BUILTIN_OBJ},
+			Fn:    callFn,
+		},
 		// split(string:"hello")
 		"split": &object.Builtin{
 			Types: []string{object.STRING_OBJ},
@@ -834,6 +839,16 @@ func typeFn(tok token.Token, env *object.Environment, args ...object.Object) obj
 	return &object.String{Token: tok, Value: string(args[0].Type())}
 }
 
+// fn.call(args_array)
+func callFn(tok token.Token, env *object.Environment, args ...object.Object) object.Object {
+	err := validateArgs(tok, "call", args, 2, [][]string{{object.FUNCTION_OBJ, object.BUILTIN_OBJ}, {object.ARRAY_OBJ}})
+	if err != nil {
+		return err
+	}
+
+	return applyFunction(tok, args[0], env, args[1].(*object.Array).Elements)
+}
+
 // split(string:"hello world!", sep:" ")
 func splitFn(tok token.Token, env *object.Environment, args ...object.Object) object.Object {
 	err, spec := validateVarArgs(tok, "split", args, [][][]string{


Test patch:
diff --git a/evaluator/evaluator_test.go b/evaluator/evaluator_test.go
index d8e9213..ca47386 100644
--- a/evaluator/evaluator_test.go
+++ b/evaluator/evaluator_test.go
@@ -976,6 +976,8 @@ c")`, []string{"a", "b", "c"}},
 		{`[[1,2,3], [2,3,4]].tsv("abc")`, "1a2a3\n2a3a4"},
 		{`[[1,2,3], [2,3,4]].tsv("")`, "the separator argument to the tsv() function needs to be a valid character, '' given"},
 		{`[{"c": 3, "b": "hello"}, {"b": 20, "c": 0}].tsv("\t", ["c", "b", "a"])`, "c\tb\ta\n3\thello\tnull\n0\t20\tnull"},
+		{`adder = f (a, b) { return a + b }; adder.call([5, 5])`, 10},
+		{`int.call(["12"])`, 12},
 	}
 	for _, tt := range tests {
 		evaluated := testEval(tt.input)
Repository: abs-lang/abs
Pull request #339 (2020-03-23 23:29:02)

number.clamp(min, max)
Clamps the number between min and max:

``` bash
10.clamp(0, 100) # 10
10.clamp(0, 5) # 5
10.clamp(50, 100) # 50
1.5.clamp(2.5, 3) # 2.5
```


Patch:
diff --git a/docs/types/number.md b/docs/types/number.md
index db56e8f..c20947e 100644
--- a/docs/types/number.md
+++ b/docs/types/number.md
@@ -102,6 +102,17 @@ Rounds the number up to the closest integer:
 10.3.ceil() # 11
 ```
 
+### clamp(min, max)
+
+Clamps the number between min and max:
+
+``` bash
+10.clamp(0, 100) # 10
+10.clamp(0, 5) # 5
+10.clamp(50, 100) # 50
+1.5.clamp(2.5, 3) # 2.5
+```
+
 ### floor()
 
 Rounds the number down to the closest integer:
diff --git a/evaluator/functions.go b/evaluator/functions.go
index 8090cd8..ecf7cc4 100644
--- a/evaluator/functions.go
+++ b/evaluator/functions.go
@@ -71,6 +71,11 @@ func getFns() map[string]*object.Builtin {
 			Types: []string{},
 			Fn:    cdFn,
 		},
+		// clamp(num, min, max)
+		"clamp": &object.Builtin{
+			Types: []string{object.NUMBER_OBJ},
+			Fn:    clampFn,
+		},
 		// echo(arg:"hello")
 		"echo": &object.Builtin{
 			Types: []string{},
@@ -618,6 +623,34 @@ func cdFn(tok token.Token, env *object.Environment, args ...object.Object) objec
 	return &object.String{Token: tok, Value: dir, Ok: &object.Boolean{Token: tok, Value: true}}
 }
 
+// clamp(n, min, max)
+func clampFn(tok token.Token, env *object.Environment, args ...object.Object) object.Object {
+	err := validateArgs(tok, "clamp", args, 3, [][]string{{object.NUMBER_OBJ}, {object.NUMBER_OBJ}, {object.NUMBER_OBJ}})
+	if err != nil {
+		return err
+	}
+
+	n := args[0].(*object.Number)
+	min := args[1].(*object.Number)
+	max := args[2].(*object.Number)
+
+	if min.Value >= max.Value {
+		return newError(tok, "arguments to clamp(min, max) must satisfy min < max (%s < %s given)", min.Inspect(), max.Inspect())
+	}
+
+	val := n.Value
+
+	if min.Value > n.Value {
+		val = min.Value
+	}
+
+	if max.Value < n.Value {
+		val = max.Value
+	}
+
+	return &object.Number{Value: val}
+}
+
 // echo(arg:"hello")
 func echoFn(tok token.Token, env *object.Environment, args ...object.Object) object.Object {
 	if len(args) == 0 {


Test patch:
diff --git a/evaluator/builtin_functions_test.go b/evaluator/builtin_functions_test.go
index 37e1b3f..14093b1 100644
--- a/evaluator/builtin_functions_test.go
+++ b/evaluator/builtin_functions_test.go
@@ -569,6 +569,25 @@ func TestBetween(t *testing.T) {
 	testBuiltinFunction(tests, t)
 }
 
+func TestClamp(t *testing.T) {
+	tests := []Tests{
+		{`2.clamp(0, 10)`, 2},
+		{`2.clamp(2, 10)`, 2},
+		{`2.clamp(3, 10)`, 3},
+		{`2.clamp(0, 3)`, 2},
+		{`2.clamp(2, 3)`, 2},
+		{`2.clamp(3, 3)`, "arguments to clamp(min, max) must satisfy min < max"},
+		{`2.clamp(3, 10)`, 3},
+		{`2.clamp(0, 1)`, 1},
+		{`2.clamp(0, 2)`, 2},
+		{`2.clamp(1.5, 2.5)`, 2},
+		{`2.clamp(2.1, 2.5)`, 2.1},
+		{`2.5.clamp(2.1, 2.3)`, 2.3},
+	}
+
+	testBuiltinFunction(tests, t)
+}
+
 func testBuiltinFunction(tests []Tests, t *testing.T) {
 	for _, tt := range tests {
 		evaluated := testEval(tt.input)
Repository: abs-lang/abs
Pull request #340 (2020-03-23 23:44:15)

number.between(min, max)
Checks whether the number is between `min` and `max`:

``` bash
10.between(0, 100) # true
10.between(10, 100) # true
10.between(11, 100) # false
```


Patch:
diff --git a/docs/types/number.md b/docs/types/number.md
index e2c3cc4..db56e8f 100644
--- a/docs/types/number.md
+++ b/docs/types/number.md
@@ -84,6 +84,16 @@ by default:
 10.333.round(1) # 10.3
 ```
 
+### between(min, max)
+
+Checks whether the number is between `min` and `max`:
+
+``` bash
+10.between(0, 100) # true
+10.between(10, 100) # true
+10.between(11, 100) # false
+```
+
 ### ceil()
 
 Rounds the number up to the closest integer:
diff --git a/evaluator/functions.go b/evaluator/functions.go
index df00f8a..8090cd8 100644
--- a/evaluator/functions.go
+++ b/evaluator/functions.go
@@ -222,6 +222,11 @@ func getFns() map[string]*object.Builtin {
 			Types: []string{object.STRING_OBJ},
 			Fn:    anyFn,
 		},
+		// between(number, min, max)
+		"between": &object.Builtin{
+			Types: []string{object.NUMBER_OBJ},
+			Fn:    betweenFn,
+		},
 		// prefix("abc", "a")
 		"prefix": &object.Builtin{
 			Types: []string{object.STRING_OBJ},
@@ -1298,6 +1303,24 @@ func anyFn(tok token.Token, env *object.Environment, args ...object.Object) obje
 	return &object.Boolean{Token: tok, Value: strings.ContainsAny(args[0].(*object.String).Value, args[1].(*object.String).Value)}
 }
 
+// between(10, 0, 100)
+func betweenFn(tok token.Token, env *object.Environment, args ...object.Object) object.Object {
+	err := validateArgs(tok, "between", args, 3, [][]string{{object.NUMBER_OBJ}, {object.NUMBER_OBJ}, {object.NUMBER_OBJ}})
+	if err != nil {
+		return err
+	}
+
+	n := args[0].(*object.Number)
+	min := args[1].(*object.Number)
+	max := args[2].(*object.Number)
+
+	if min.Value >= max.Value {
+		return newError(tok, "arguments to between(min, max) must satisfy min < max (%s < %s given)", min.Inspect(), max.Inspect())
+	}
+
+	return &object.Boolean{Token: tok, Value: ((min.Value <= n.Value) && (n.Value <= max.Value))}
+}
+
 // prefix("abc", "a")
 func prefixFn(tok token.Token, env *object.Environment, args ...object.Object) object.Object {
 	err := validateArgs(tok, "prefix", args, 2, [][]string{{object.STRING_OBJ}, {object.STRING_OBJ}})


Test patch:
diff --git a/evaluator/builtin_functions_test.go b/evaluator/builtin_functions_test.go
index 46153ea..37e1b3f 100644
--- a/evaluator/builtin_functions_test.go
+++ b/evaluator/builtin_functions_test.go
@@ -557,6 +557,18 @@ func TestChunk(t *testing.T) {
 	testBuiltinFunction(tests, t)
 }
 
+func TestBetween(t *testing.T) {
+	tests := []Tests{
+		{`1.between(0, 2)`, true},
+		{`1.between(0, 1.1)`, true},
+		{`1.between(0, 0.9)`, false},
+		{`1.between(1, 0)`, "arguments to between(min, max) must satisfy min < max (1 < 0 given)"},
+		{`1.between(1, 2)`, true},
+	}
+
+	testBuiltinFunction(tests, t)
+}
+
 func testBuiltinFunction(tests []Tests, t *testing.T) {
 	for _, tt := range tests {
 		evaluated := testEval(tt.input)
Repository: abs-lang/abs
Pull request #364 (2020-04-04 15:58:16)

Fixed panic when converting index ranges to string


Patch:
diff --git a/ast/ast.go b/ast/ast.go
index 5e2d684..ef9e531 100644
--- a/ast/ast.go
+++ b/ast/ast.go
@@ -543,7 +543,18 @@ func (ie *IndexExpression) String() string {
 	out.WriteString("[")
 
 	if ie.IsRange {
-		out.WriteString(ie.Index.String() + ":" + ie.End.String())
+		start := ""
+
+		if ie.Index != nil {
+			start = ie.Index.String()
+		}
+
+		end := ""
+
+		if ie.End != nil {
+			end = ie.End.String()
+		}
+		out.WriteString(start + ":" + end)
 	} else {
 		out.WriteString(ie.Index.String())
 	}


Test patch:
diff --git a/evaluator/builtin_functions_test.go b/evaluator/builtin_functions_test.go
index 46b960c..b6537e8 100644
--- a/evaluator/builtin_functions_test.go
+++ b/evaluator/builtin_functions_test.go
@@ -258,6 +258,8 @@ func TestStr(t *testing.T) {
 		{`1.str()`, "1"},
 		{`[1].str()`, "[1]"},
 		{`{"a": 10}.str()`, `{"a": 10}`},
+		{`f() {a[3:]}.str()`, `f() {(a[3:])}`},
+		{`f() {a[:3]}.str()`, `f() {(a[0:3])}`},
 	}
 
 	testBuiltinFunction(tests, t)
Repository: abs-lang/abs
Pull request #367 (2020-05-12 23:37:51)

Number ranges should work on descending order as well
Just like `1..10`, we should be able to create lists with
`10..1`.


Patch:
diff --git a/docs/syntax/operators.md b/docs/syntax/operators.md
index d3f2ad2..b7f5d23 100644
--- a/docs/syntax/operators.md
+++ b/docs/syntax/operators.md
@@ -278,7 +278,8 @@ test?.something?.something_else() # null
 Range operator, which creates an array from start to end:
 
 ``` bash
-1..10 # [1, 2, 3, 4, 5, 6, 7, 8, 9]
+1..10 # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+10..1 # [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
 ```
 
 ## !
diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go
index 3b08e5b..059df37 100644
--- a/evaluator/evaluator.go
+++ b/evaluator/evaluator.go
@@ -661,9 +661,16 @@ func evalNumberInfixExpression(
 	case "..":
 		a := make([]object.Object, 0)
 
-		for i := leftVal; i <= rightVal; i++ {
-			a = append(a, &object.Number{Token: tok, Value: float64(i)})
+		if leftVal <= rightVal {
+			for i := leftVal; i <= rightVal; i++ {
+				a = append(a, &object.Number{Token: tok, Value: float64(i)})
+			}
+		} else {
+			for i := leftVal; i >= rightVal; i-- {
+				a = append(a, &object.Number{Token: tok, Value: float64(i)})
+			}
 		}
+
 		return &object.Array{Token: tok, Elements: a}
 	default:
 		return newError(tok, "unknown operator: %s %s %s", left.Type(), operator, right.Type())


Test patch:
diff --git a/evaluator/evaluator_test.go b/evaluator/evaluator_test.go
index 4006e9b..475b64f 100644
--- a/evaluator/evaluator_test.go
+++ b/evaluator/evaluator_test.go
@@ -460,7 +460,7 @@ func TestForElseExpressions(t *testing.T) {
 		expected interface{}
 	}{
 		{"a = 0; b = 1; for v in [] { x = a } else { x = b }; x", 1},
-		{"a = 100; x = 0; for i in  1..-1 { x = i } else { x = a }; x", 100},
+		{"a = 100; x = 0; for i in  'abcd'.split('').filter(is_number) { x = i } else { x = a }; x", 100},
 		{"v = 100; for k, v in [] { v = 0 } else {}; v", 100},
 		{"for k, v in [] {} else { x = v }; x", "identifier not found: v"},
 		{"a = 0; for k, v in [] { x = a } else { x = b }; x", "identifier not found: b"},
@@ -958,10 +958,11 @@ func TestRangesOperators(t *testing.T) {
 		input    string
 		expected interface{}
 	}{
-		{`1..0`, []int{}},
+		{`1..0`, []int{1, 0}},
 		{`-1..0`, []int{-1, 0}},
 		{`1..1`, []int{1}},
 		{`1..2`, []int{1, 2}},
+		{`2..1`, []int{2, 1}},
 		{`len("a")..len("aa")`, []int{1, 2}},
 	}
 	for _, tt := range tests {
Repository: pipxproject/pipx
Pull request #308 (2020-01-03 05:38:48)

[bugfix] fix 'delay' in animate api
Delay is an argument to functions, but wasn't used. This PR actually implements the delay.

itsayellow: The code LGTM.

I would think adding at least one test is nice.  Could you just make a test that delays the start of animation, and the code inside the context finishes before that delay, so that you can verify that there was no animation printed?  I think that might be the best we can do, given threads/timing/precision constraints.

Patch:
diff --git a/src/pipx/animate.py b/src/pipx/animate.py
index 14ac656..1f860d1 100644
--- a/src/pipx/animate.py
+++ b/src/pipx/animate.py
@@ -19,7 +19,9 @@ NONEMOJI_FRAME_PERIOD = 1
 
 
 @contextmanager
-def animate(message: str, do_animation: bool) -> Generator[None, None, None]:
+def animate(
+    message: str, do_animation: bool, *, delay: float = 0
+) -> Generator[None, None, None]:
 
     if not do_animation or not stderr_is_tty:
         # no op
@@ -41,7 +43,7 @@ def animate(message: str, do_animation: bool) -> Generator[None, None, None]:
         "message": message,
         "event": event,
         "symbols": symbols,
-        "delay": 0,
+        "delay": delay,
         "period": period,
         "animate_at_beginning_of_line": animate_at_beginning_of_line,
     }
@@ -70,6 +72,7 @@ def print_animation(
     animate_at_beginning_of_line: bool,
 ):
     (term_cols, _) = shutil.get_terminal_size(fallback=(9999, 24))
+    event.wait(delay)
     while not event.wait(0):
         for s in symbols:
             if animate_at_beginning_of_line:


Test patch:
diff --git a/tests/test_animate.py b/tests/test_animate.py
index acba096..46927e8 100644
--- a/tests/test_animate.py
+++ b/tests/test_animate.py
@@ -25,6 +25,17 @@ def check_animate_output(
     assert captured.err[:chars_to_test] == expected_string[:chars_to_test]
 
 
+def test_delay_suppresses_output(capsys, monkeypatch):
+    monkeypatch.setattr(pipx.animate, "stderr_is_tty", True)
+
+    test_string = "asdf"
+
+    with pipx.animate.animate(test_string, do_animation=True, delay=0.9):
+        time.sleep(0.5)
+    captured = capsys.readouterr()
+    assert test_string not in captured.err
+
+
 def test_line_lengths_emoji(capsys, monkeypatch):
     # emoji_support and stderr_is_tty is set only at import animate.py
     # since we are already after that, we must override both here
Repository: pipxproject/pipx
Pull request #383 (2020-04-04 18:59:10)

hide cursor while pipx is running
<!---
Thank you for your soon-to-be pull request. Before you submit this, please
double check to make sure that you've added an entry to docs/changelog.md.
-->
Instead of hiding/showing the cursor each time an animation is started/finished, just hide it for the entire time pipx runs, then show it when it finishes. This stops it from showing up and disappearing in the middle of multiple operations, which looks unpolished.


Patch:
diff --git a/docs/changelog.md b/docs/changelog.md
index 17290d3..cc726ef 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -5,6 +5,7 @@ dev
 - [bugfix] Replaced implicit dependency on setuptools with an explicit dependency on packaging (#339).
 - [refactor] Moved all commands to separate files within the commands module (#255).
 - [bugfix] Continue reinstalling packages after failure
+- [bugfix] Hide cursor while pipx runs
 
 0.15.1.3
 
diff --git a/src/pipx/animate.py b/src/pipx/animate.py
index 1f860d1..2b1567c 100644
--- a/src/pipx/animate.py
+++ b/src/pipx/animate.py
@@ -48,7 +48,6 @@ def animate(
         "animate_at_beginning_of_line": animate_at_beginning_of_line,
     }
 
-    hide_cursor()
     t = Thread(target=print_animation, kwargs=thread_kwargs)
     t.start()
 
@@ -57,7 +56,6 @@ def animate(
     finally:
         event.set()
         clear_line()
-        show_cursor()
         sys.stderr.write("\r")
         sys.stdout.write("\r")
 
diff --git a/src/pipx/main.py b/src/pipx/main.py
index c821803..95989f1 100644
--- a/src/pipx/main.py
+++ b/src/pipx/main.py
@@ -20,6 +20,7 @@ from . import constants
 from .util import PipxError, mkdir
 from .venv import VenvContainer
 from .version import __version__
+from .animate import hide_cursor, show_cursor
 
 
 def print_version() -> None:
@@ -563,6 +564,7 @@ def check_args(parsed_pipx_args: argparse.Namespace):
 def cli() -> int:
     """Entry point from command line"""
     try:
+        hide_cursor()
         parser = get_command_parser()
         argcomplete.autocomplete(parser)
         parsed_pipx_args = parser.parse_args()
@@ -577,6 +579,8 @@ def cli() -> int:
         return 1
     except KeyboardInterrupt:
         return 1
+    finally:
+        show_cursor()
 
 
 if __name__ == "__main__":


Test patch:
diff --git a/tests/test_animate.py b/tests/test_animate.py
index 46927e8..41c3ba3 100644
--- a/tests/test_animate.py
+++ b/tests/test_animate.py
@@ -2,7 +2,6 @@ import time
 
 import pipx.animate
 from pipx.animate import (
-    HIDE_CURSOR,
     CLEAR_LINE,
     EMOJI_ANIMATION_FRAMES,
     NONEMOJI_ANIMATION_FRAMES,
@@ -14,7 +13,7 @@ from pipx.animate import (
 def check_animate_output(
     capsys, test_string, frame_strings, frame_period, frames_to_test
 ):
-    expected_string = f"{HIDE_CURSOR}" + "".join(frame_strings)
+    expected_string = "".join(frame_strings)
 
     chars_to_test = 1 + len("".join(frame_strings[:frames_to_test]))