Skip to content

Commit 9e25df5

Browse files
search5claude
andcommitted
Release v1.0.6
- Add URL validation: warn if path doesn't contain /solr - Prepare for Solr 10.0+ root URL restriction Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 962d986 commit 9e25df5

8 files changed

Lines changed: 93 additions & 5 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ poetry run pytest tests/
102102

103103
## Changelog
104104

105+
### 1.0.6
106+
107+
- URL validation: warns if URL path doesn't contain `/solr` (Solr 10.0+ preparation)
108+
105109
### 1.0.5
106110

107111
- **Breaking**: Removed `SolrConnection` class. Use `Solr` instead

docs/changelog.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
Changelog
22
=========
33

4+
1.0.6 (2026-03-27)
5+
-------------------
6+
7+
**New features:**
8+
9+
- ``Solr`` constructor now validates the URL path. A ``UserWarning`` is issued
10+
if the path does not contain ``/solr``, preparing for Solr 10.0+ which
11+
requires the URL to end with ``/solr``.
12+
13+
414
1.0.5 (2026-03-27)
515
-------------------
616

docs/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
copyright = '2010-2026, solrpy developers'
1010
author = 'solrpy developers'
1111

12-
version = '1.0.5'
13-
release = '1.0.5'
12+
version = '1.0.6'
13+
release = '1.0.6'
1414

1515
# -- General configuration ---------------------------------------------------
1616

docs/quickstart.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ The connected Solr version is auto-detected::
2626

2727
print(conn.server_version) # e.g. (9, 4, 1)
2828

29+
.. note::
30+
31+
If the URL does not contain ``/solr`` in its path, a ``UserWarning``
32+
is issued. Solr 10.0+ requires the URL to end with ``/solr``.
33+
2934

3035
Health check
3136
------------

docs/reference.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ Solr class
6161
* - Parameter
6262
- Description
6363
* - ``url``
64-
- URI pointing to the Solr instance (e.g. ``http://localhost:8983/solr/mycore``)
64+
- URI pointing to the Solr instance (e.g. ``http://localhost:8983/solr/mycore``).
65+
A ``UserWarning`` is issued if the path does not contain ``/solr``.
6566
* - ``persistent``
6667
- Keep a persistent HTTP connection open. Defaults to ``True``.
6768
* - ``timeout``

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "solrpy"
3-
version = "1.0.5"
3+
version = "1.0.6"
44
description = "Client for the Solr search service"
55
license = "Apache-2.0"
66
readme = "README.md"

solr/core.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import socket
66
import datetime
77
import logging
8+
import warnings
89
import base64
910
import http.client as httplib
1011
import urllib.parse as urlparse
@@ -21,7 +22,7 @@
2122
from .response import Response, Results
2223
from .parsers import parse_json_response, parse_query_response
2324

24-
__version__ = "1.0.5"
25+
__version__ = "1.0.6"
2526

2627
__all__ = ['SolrException', 'SolrVersionError', 'Solr',
2728
'Response', 'SearchHandler']
@@ -51,6 +52,19 @@ def __init__(self, url: str,
5152

5253
assert self.scheme in ('http', 'https')
5354

55+
# Validate URL path contains /solr
56+
path_parts = self.path.rstrip('/').split('/')
57+
if 'solr' not in path_parts:
58+
warnings.warn(
59+
"solrpy: URL '%s' does not contain '/solr' in its path. "
60+
"Expected format: http://host:port/solr or "
61+
"http://host:port/solr/<core>. "
62+
"Solr 10.0+ requires the URL to end with '/solr'."
63+
% url,
64+
UserWarning,
65+
stacklevel=2,
66+
)
67+
5468
self.persistent = persistent
5569
self.reconnects = 0
5670
self.timeout = timeout

tests/test_all.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2155,5 +2155,59 @@ def test_json_query_has_header(self):
21552155
conn.close()
21562156

21572157

2158+
# ===================================================================
2159+
# 1.0.6 tests — URL validation
2160+
# ===================================================================
2161+
2162+
class TestURLValidation(unittest.TestCase):
2163+
"""Solr constructor should warn on suspicious URLs."""
2164+
2165+
def test_valid_url_no_warning(self):
2166+
import warnings
2167+
with warnings.catch_warnings(record=True) as w:
2168+
warnings.simplefilter("always")
2169+
conn = solr.Solr(SOLR_HTTP, response_format='xml')
2170+
solr_warnings = [x for x in w if 'solrpy' in str(x.message)]
2171+
self.assertEqual(len(solr_warnings), 0)
2172+
conn.close()
2173+
2174+
def test_url_ending_with_solr_no_warning(self):
2175+
import warnings
2176+
with warnings.catch_warnings(record=True) as w:
2177+
warnings.simplefilter("always")
2178+
conn = solr.Solr('http://localhost:8983/solr', response_format='xml')
2179+
solr_warnings = [x for x in w if 'solrpy' in str(x.message)]
2180+
self.assertEqual(len(solr_warnings), 0)
2181+
conn.close()
2182+
2183+
def test_suspicious_url_warns(self):
2184+
import warnings
2185+
with warnings.catch_warnings(record=True) as w:
2186+
warnings.simplefilter("always")
2187+
conn = solr.Solr('http://localhost:8983/search', response_format='xml')
2188+
solr_warnings = [x for x in w if 'solrpy' in str(x.message)]
2189+
self.assertEqual(len(solr_warnings), 1)
2190+
self.assertIn('/solr', str(solr_warnings[0].message))
2191+
conn.close()
2192+
2193+
def test_root_url_warns(self):
2194+
import warnings
2195+
with warnings.catch_warnings(record=True) as w:
2196+
warnings.simplefilter("always")
2197+
conn = solr.Solr('http://localhost:8983/', response_format='xml')
2198+
solr_warnings = [x for x in w if 'solrpy' in str(x.message)]
2199+
self.assertEqual(len(solr_warnings), 1)
2200+
conn.close()
2201+
2202+
def test_url_with_solr_in_path_no_warning(self):
2203+
import warnings
2204+
with warnings.catch_warnings(record=True) as w:
2205+
warnings.simplefilter("always")
2206+
conn = solr.Solr('http://localhost:8983/solr/mycore', response_format='xml')
2207+
solr_warnings = [x for x in w if 'solrpy' in str(x.message)]
2208+
self.assertEqual(len(solr_warnings), 0)
2209+
conn.close()
2210+
2211+
21582212
if __name__ == "__main__":
21592213
unittest.main()

0 commit comments

Comments
 (0)