Repository: aio-libs/yarl

Issue:
regression in URL.build(…,port="") 
### Describe the bug

Input filtering for with_port #793 changed the logic to render the URL to compare the port to None.
https://github.com/aio-libs/yarl/commit/94b8679da05fd7b2d7bc86ed95e84c09f1946d75#r116844287
as it is possible to create a URL with a port="" via URL.build, this results in urls created with an empty port.


### To Reproduce

```
import yarl
u = yarl.URL.build(scheme="http", host="api.com", port="", path="/v1")
assert str(u) == "http://api.com/v1", str(u)
```

### Expected behavior

Filtering port="" in URL.build.
I'd even prefer to allow "" and convert to None to stay backwards compatible.

### Logs/tracebacks

```python-traceback
AssertionError: http://api.com:/v1
```


### Python Version

```console
$ python --version
Python 3.10.6
```


### multidict Version

```console
$ python -m pip show multidict
Name: multidict
Version: 6.0.2
```


### yarl Version

```console
$ python -m pip show yarl
Name: yarl
Version: 1.9.2
```


### OS

Linux

### Additional context

_No response_

### Code of Conduct

- [X] I agree to follow the aio-libs Code of Conduct

Comments:

Patch:
diff --git a/CHANGES/883.bugfix b/CHANGES/883.bugfix
new file mode 100644
index 0000000..9864377
--- /dev/null
+++ b/CHANGES/883.bugfix
@@ -0,0 +1,4 @@
+Started raising :py:exc:`TypeError` when a string value is passed into
+:py:meth:`~yarl.URL.build` as the ``port`` argument  -- by :user:`commonism`.
+
+Previously the empty string as port would create malformed URLs when rendered as string representations.
diff --git a/yarl/_url.py b/yarl/_url.py
index c8f2acb..d5202db 100644
--- a/yarl/_url.py
+++ b/yarl/_url.py
@@ -233,6 +233,8 @@ class URL:
             raise ValueError(
                 'Can\'t mix "authority" with "user", "password", "host" or "port".'
             )
+        if not isinstance(port, (int, type(None))):
+            raise TypeError("The port is required to be int.")
         if port and not host:
             raise ValueError('Can\'t build URL with "port" but without "host".')
         if query and query_string:


Test patch:
diff --git a/tests/test_url_build.py b/tests/test_url_build.py
index 51969fa..ed07736 100644
--- a/tests/test_url_build.py
+++ b/tests/test_url_build.py
@@ -32,12 +32,28 @@ def test_build_with_scheme_and_host():
     assert u == URL("http://127.0.0.1")
 
 
-def test_build_with_port():
-    with pytest.raises(ValueError):
-        URL.build(port=8000)
-
-    u = URL.build(scheme="http", host="127.0.0.1", port=8000)
-    assert str(u) == "http://127.0.0.1:8000"
+@pytest.mark.parametrize(
+    ("port", "exc", "match"),
+    [
+        pytest.param(
+            8000,
+            ValueError,
+            r"""(?x)
+            ^
+            Can't\ build\ URL\ with\ "port"\ but\ without\ "host"\.
+            $
+            """,
+            id="port-only",
+        ),
+        pytest.param(
+            "", TypeError, r"^The port is required to be int\.$", id="port-str"
+        ),
+    ],
+)
+def test_build_with_port(port, exc, match):
+    print(match)
+    with pytest.raises(exc, match=match):
+        URL.build(port=port)
 
 
 def test_build_with_user():