moy: Looks good to me.
diff --git a/mechanicalsoup/stateful_browser.py b/mechanicalsoup/stateful_browser.py
index a233f6d..b96dc1b 100644
--- a/mechanicalsoup/stateful_browser.py
+++ b/mechanicalsoup/stateful_browser.py
@@ -177,7 +177,8 @@ class StatefulBrowser(Browser):
     def select_form(self, selector="form", nr=0):
         """Select a form in the current page.
 
-        :param selector: CSS selector to identify the form to select.
+        :param selector: CSS selector or a bs4.element.Tag object to identify
+            the form to select.
             If not specified, ``selector`` defaults to "form", which is
             useful if, e.g., there is only one form on the page.
             For ``selector`` syntax, see the `.select() method in BeautifulSoup
@@ -191,8 +192,17 @@ class StatefulBrowser(Browser):
         :return: The selected form as a soup object. It can also be
             retrieved later with :func:`get_current_form`.
         """
-        # nr is a 0-based index for consistency with mechanize
-        found_forms = self.get_current_page().select(selector, limit=nr + 1)
+        if isinstance(selector, bs4.element.Tag):
+            if selector.name == "form":
+                self.__state.form = Form(selector)
+                return self.get_current_form()
+            else:
+                raise LinkNotFoundError()
+        else:
+            # nr is a 0-based index for consistency with mechanize
+            found_forms = self.get_current_page().select(selector,
+                                                         limit=nr + 1)
+
         if len(found_forms) != nr + 1:
             if self.__debug:
                 print('select_form failed for', selector)

Allow passing a Tag object to StatefulBrowser.select_form
Hi,
I've recently had to select a form based on its children and the only way I found to do that is:
```py
buttons = browser.get_current_page().select("form > button[name=apply]")
browser._StatefulBrowser__current_form = mechanicalsoup.Form(buttons[0].parent)
browser.submit_selected()
```
Could you consider adding something like `if isinstance(selector, bs4.element.Tag): …` to `select_form` ?
diff --git a/tests/test_stateful_browser.py b/tests/test_stateful_browser.py
index 942ea0b..fba91c9 100644
--- a/tests/test_stateful_browser.py
+++ b/tests/test_stateful_browser.py
@@ -353,6 +353,18 @@ def test_select_form_nr():
             browser.select_form(nr=3)
 
 
+def test_select_form_tag_object():
+    """Test tag object as selector parameter type"""
+    forms = """<form id="a"></form><form id="b"></form><p></p>"""
+    soup = BeautifulSoup(forms, "lxml")
+    with mechanicalsoup.StatefulBrowser() as browser:
+        browser.open_fake_page(forms)
+        form = browser.select_form(soup.find("form", {"id": "b"}))
+        assert form.form['id'] == "b"
+        with pytest.raises(mechanicalsoup.LinkNotFoundError):
+            browser.select_form(soup.find("p"))
+
+
 def test_referer_follow_link(httpbin):
     browser = mechanicalsoup.StatefulBrowser()
     browser.open(httpbin.url)

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest pytest-cov pytest-flake8 pytest-mock
3.6
requirements.txt tests/requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs==22.2.0
beautifulsoup4==4.12.3
certifi==2021.5.30
charset-normalizer==2.0.12
coverage==6.2
flake8==5.0.4
idna==3.10
importlib-metadata==4.2.0
iniconfig==1.1.1
lxml==5.3.1
mccabe==0.7.0
-e git+https://github.com/MechanicalSoup/MechanicalSoup.git@7171e2b79ba0fbe02a7245066cb5536ddb2fe94e#egg=MechanicalSoup
packaging==21.3
pluggy==1.0.0
py==1.11.0
pycodestyle==2.9.1
pyflakes==2.5.0
pyparsing==3.1.4
pytest==7.0.1
pytest-cov==4.0.0
pytest-flake8==1.1.1
pytest-mock==3.6.1
requests==2.27.1
requests-mock==1.12.1
six==1.17.0
soupsieve==2.3.2.post1
tomli==1.2.3
typing_extensions==4.1.1
urllib3==1.26.20
zipp==3.6.0

name: MechanicalSoup
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - attrs==22.2.0
      - beautifulsoup4==4.12.3
      - charset-normalizer==2.0.12
      - coverage==6.2
      - flake8==5.0.4
      - idna==3.10
      - importlib-metadata==4.2.0
      - iniconfig==1.1.1
      - lxml==5.3.1
      - mccabe==0.7.0
      - packaging==21.3
      - pluggy==1.0.0
      - py==1.11.0
      - pycodestyle==2.9.1
      - pyflakes==2.5.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - pytest-cov==4.0.0
      - pytest-flake8==1.1.1
      - pytest-mock==3.6.1
      - requests==2.27.1
      - requests-mock==1.12.1
      - six==1.17.0
      - soupsieve==2.3.2.post1
      - tomli==1.2.3
      - typing-extensions==4.1.1
      - urllib3==1.26.20
      - zipp==3.6.0
prefix: /opt/conda/envs/MechanicalSoup

tests/test_stateful_browser.py::test_select_form_tag_object
tests/test_stateful_browser.py::flake-8::FLAKE8 tests/test_stateful_browser.py::test_submit_online tests/test_stateful_browser.py::test_form_noaction tests/test_stateful_browser.py::test_referer_follow_link tests/test_stateful_browser.py::test_download_link tests/test_stateful_browser.py::test_download_link_nofile tests/test_stateful_browser.py::test_download_link_to_existing_file
tests/test_stateful_browser.py::test_request_forward tests/test_stateful_browser.py::test_no_404 tests/test_stateful_browser.py::test_404 tests/test_stateful_browser.py::test_user_agent tests/test_stateful_browser.py::test_open_relative tests/test_stateful_browser.py::test_links tests/test_stateful_browser.py::test_submit_btnName[input] tests/test_stateful_browser.py::test_submit_btnName[button] tests/test_stateful_browser.py::test_get_set_debug tests/test_stateful_browser.py::test_list_links tests/test_stateful_browser.py::test_launch_browser tests/test_stateful_browser.py::test_find_link tests/test_stateful_browser.py::test_verbose tests/test_stateful_browser.py::test_new_control tests/test_stateful_browser.py::test_form_noname tests/test_stateful_browser.py::test_form_multiple tests/test_stateful_browser.py::test_upload_file tests/test_stateful_browser.py::test_with tests/test_stateful_browser.py::test_select_form_nr tests/test_stateful_browser.py::test_referer_submit tests/test_stateful_browser.py::test_referer_submit_headers tests/test_stateful_browser.py::test_link_arg_text tests/test_stateful_browser.py::test_link_arg_regex tests/test_stateful_browser.py::test_link_arg_multiregex tests/test_stateful_browser.py::test_download_link_404 tests/test_stateful_browser.py::test_download_link_referer tests/test_stateful_browser.py::test_refresh_open tests/test_stateful_browser.py::test_refresh_follow_link tests/test_stateful_browser.py::test_refresh_form_not_retained tests/test_stateful_browser.py::test_refresh_error
mechanicalsoup/stateful_browser.py
mechanicalsoup/stateful_browser.py