google-news-trends-mcp 0.1.5__py3-none-any.whl → 0.1.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- google_news_trends_mcp/news.py +80 -20
- google_news_trends_mcp/server.py +82 -6
- {google_news_trends_mcp-0.1.5.dist-info → google_news_trends_mcp-0.1.7.dist-info}/METADATA +1 -2
- google_news_trends_mcp-0.1.7.dist-info/RECORD +11 -0
- google_news_trends_mcp-0.1.5.dist-info/RECORD +0 -11
- {google_news_trends_mcp-0.1.5.dist-info → google_news_trends_mcp-0.1.7.dist-info}/WHEEL +0 -0
- {google_news_trends_mcp-0.1.5.dist-info → google_news_trends_mcp-0.1.7.dist-info}/entry_points.txt +0 -0
- {google_news_trends_mcp-0.1.5.dist-info → google_news_trends_mcp-0.1.7.dist-info}/licenses/LICENSE +0 -0
- {google_news_trends_mcp-0.1.5.dist-info → google_news_trends_mcp-0.1.7.dist-info}/top_level.txt +0 -0
google_news_trends_mcp/news.py
CHANGED
@@ -17,10 +17,11 @@ import cloudscraper
|
|
17
17
|
from playwright.async_api import async_playwright, Browser, Playwright
|
18
18
|
from trendspy import Trends, TrendKeyword
|
19
19
|
import click
|
20
|
-
from typing import Optional, cast
|
20
|
+
from typing import Optional, cast, overload, Literal, Awaitable
|
21
21
|
import atexit
|
22
22
|
from contextlib import asynccontextmanager
|
23
23
|
import logging
|
24
|
+
from collections.abc import Callable
|
24
25
|
|
25
26
|
logger = logging.getLogger(__name__)
|
26
27
|
|
@@ -53,6 +54,8 @@ google_news = GNews(
|
|
53
54
|
playwright: Optional[Playwright] = None
|
54
55
|
browser: Optional[Browser] = None
|
55
56
|
|
57
|
+
ProgressCallback = Callable[[float, Optional[float]], Awaitable[None]]
|
58
|
+
|
56
59
|
|
57
60
|
async def startup_browser():
|
58
61
|
global playwright, browser
|
@@ -97,7 +100,9 @@ async def download_article_with_playwright(url) -> newspaper.Article | None:
|
|
97
100
|
article = newspaper.article(url, input_html=content, language="en")
|
98
101
|
return article
|
99
102
|
except Exception as e:
|
100
|
-
logging.warning(
|
103
|
+
logging.warning(
|
104
|
+
f"Error downloading article with Playwright from {url}\n {e.args}"
|
105
|
+
)
|
101
106
|
return None
|
102
107
|
|
103
108
|
|
@@ -130,7 +135,9 @@ async def download_article(url: str, nlp: bool = True) -> newspaper.Article | No
|
|
130
135
|
f"Failed to download article with cloudscraper from {url}, status code: {response.status_code}"
|
131
136
|
)
|
132
137
|
except Exception as e:
|
133
|
-
logging.debug(
|
138
|
+
logging.debug(
|
139
|
+
f"Error downloading article with cloudscraper from {url}\n {e.args}"
|
140
|
+
)
|
134
141
|
|
135
142
|
try:
|
136
143
|
if article is None or not article.text:
|
@@ -148,23 +155,35 @@ async def download_article(url: str, nlp: bool = True) -> newspaper.Article | No
|
|
148
155
|
|
149
156
|
|
150
157
|
async def process_gnews_articles(
|
151
|
-
gnews_articles: list[dict],
|
152
|
-
|
158
|
+
gnews_articles: list[dict],
|
159
|
+
nlp: bool = True,
|
160
|
+
report_progress: Optional[ProgressCallback] = None,
|
161
|
+
) -> list[newspaper.Article]:
|
153
162
|
"""
|
154
163
|
Process a list of Google News articles and download them (async).
|
164
|
+
Optionally report progress via report_progress callback.
|
155
165
|
"""
|
156
166
|
articles = []
|
157
|
-
|
167
|
+
total = len(gnews_articles)
|
168
|
+
for idx, gnews_article in enumerate(gnews_articles):
|
158
169
|
article = await download_article(gnews_article["url"], nlp=nlp)
|
159
170
|
if article is None or not article.text:
|
160
|
-
logging.debug(
|
171
|
+
logging.debug(
|
172
|
+
f"Failed to download article from {gnews_article['url']}:\n{article}"
|
173
|
+
)
|
161
174
|
continue
|
162
175
|
articles.append(article)
|
176
|
+
if report_progress:
|
177
|
+
await report_progress(idx, total)
|
163
178
|
return articles
|
164
179
|
|
165
180
|
|
166
181
|
async def get_news_by_keyword(
|
167
|
-
keyword: str,
|
182
|
+
keyword: str,
|
183
|
+
period=7,
|
184
|
+
max_results: int = 10,
|
185
|
+
nlp: bool = True,
|
186
|
+
report_progress: Optional[ProgressCallback] = None,
|
168
187
|
) -> list[newspaper.Article]:
|
169
188
|
"""
|
170
189
|
Find articles by keyword using Google News.
|
@@ -177,14 +196,21 @@ async def get_news_by_keyword(
|
|
177
196
|
google_news.max_results = max_results
|
178
197
|
gnews_articles = google_news.get_news(keyword)
|
179
198
|
if not gnews_articles:
|
180
|
-
logging.debug(
|
199
|
+
logging.debug(
|
200
|
+
f"No articles found for keyword '{keyword}' in the last {period} days."
|
201
|
+
)
|
181
202
|
return []
|
182
|
-
return await process_gnews_articles(
|
203
|
+
return await process_gnews_articles(
|
204
|
+
gnews_articles, nlp=nlp, report_progress=report_progress
|
205
|
+
)
|
183
206
|
|
184
207
|
|
185
208
|
async def get_top_news(
|
186
|
-
period: int = 3,
|
187
|
-
|
209
|
+
period: int = 3,
|
210
|
+
max_results: int = 10,
|
211
|
+
nlp: bool = True,
|
212
|
+
report_progress: Optional[ProgressCallback] = None,
|
213
|
+
) -> list[newspaper.Article]:
|
188
214
|
"""
|
189
215
|
Get top news stories from Google News.
|
190
216
|
period: is the number of days to look back for top articles.
|
@@ -197,11 +223,17 @@ async def get_top_news(
|
|
197
223
|
if not gnews_articles:
|
198
224
|
logging.debug("No top news articles found.")
|
199
225
|
return []
|
200
|
-
return await process_gnews_articles(
|
226
|
+
return await process_gnews_articles(
|
227
|
+
gnews_articles, nlp=nlp, report_progress=report_progress
|
228
|
+
)
|
201
229
|
|
202
230
|
|
203
231
|
async def get_news_by_location(
|
204
|
-
location: str,
|
232
|
+
location: str,
|
233
|
+
period=7,
|
234
|
+
max_results: int = 10,
|
235
|
+
nlp: bool = True,
|
236
|
+
report_progress: Optional[ProgressCallback] = None,
|
205
237
|
) -> list[newspaper.Article]:
|
206
238
|
"""Find articles by location using Google News.
|
207
239
|
location: is the name of city/state/country
|
@@ -213,13 +245,21 @@ async def get_news_by_location(
|
|
213
245
|
google_news.max_results = max_results
|
214
246
|
gnews_articles = google_news.get_news_by_location(location)
|
215
247
|
if not gnews_articles:
|
216
|
-
logging.debug(
|
248
|
+
logging.debug(
|
249
|
+
f"No articles found for location '{location}' in the last {period} days."
|
250
|
+
)
|
217
251
|
return []
|
218
|
-
return await process_gnews_articles(
|
252
|
+
return await process_gnews_articles(
|
253
|
+
gnews_articles, nlp=nlp, report_progress=report_progress
|
254
|
+
)
|
219
255
|
|
220
256
|
|
221
257
|
async def get_news_by_topic(
|
222
|
-
topic: str,
|
258
|
+
topic: str,
|
259
|
+
period=7,
|
260
|
+
max_results: int = 10,
|
261
|
+
nlp: bool = True,
|
262
|
+
report_progress: Optional[ProgressCallback] = None,
|
223
263
|
) -> list[newspaper.Article]:
|
224
264
|
"""Find articles by topic using Google News.
|
225
265
|
topic is one of
|
@@ -239,9 +279,27 @@ async def get_news_by_topic(
|
|
239
279
|
google_news.max_results = max_results
|
240
280
|
gnews_articles = google_news.get_news_by_topic(topic)
|
241
281
|
if not gnews_articles:
|
242
|
-
logging.debug(
|
282
|
+
logging.debug(
|
283
|
+
f"No articles found for topic '{topic}' in the last {period} days."
|
284
|
+
)
|
243
285
|
return []
|
244
|
-
return await process_gnews_articles(
|
286
|
+
return await process_gnews_articles(
|
287
|
+
gnews_articles, nlp=nlp, report_progress=report_progress
|
288
|
+
)
|
289
|
+
|
290
|
+
|
291
|
+
@overload
|
292
|
+
async def get_trending_terms(
|
293
|
+
geo: str = "US", full_data: Literal[False] = False, max_results: int = 100
|
294
|
+
) -> list[dict[str, int]]:
|
295
|
+
pass
|
296
|
+
|
297
|
+
|
298
|
+
@overload
|
299
|
+
async def get_trending_terms(
|
300
|
+
geo: str = "US", full_data: Literal[True] = True, max_results: int = 100
|
301
|
+
) -> list[TrendKeyword]:
|
302
|
+
pass
|
245
303
|
|
246
304
|
|
247
305
|
async def get_trending_terms(
|
@@ -260,7 +318,9 @@ async def get_trending_terms(
|
|
260
318
|
:max_results
|
261
319
|
]
|
262
320
|
if not full_data:
|
263
|
-
return [
|
321
|
+
return [
|
322
|
+
{"keyword": trend.keyword, "volume": trend.volume} for trend in trends
|
323
|
+
]
|
264
324
|
return trends
|
265
325
|
except Exception as e:
|
266
326
|
logging.warning(f"Error fetching trending terms: {e}")
|
google_news_trends_mcp/server.py
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
-
from fastmcp import FastMCP
|
1
|
+
from fastmcp import FastMCP, Context
|
2
2
|
from fastmcp.exceptions import ToolError
|
3
3
|
from fastmcp.server.dependencies import get_context
|
4
4
|
from pydantic import BaseModel, Field
|
5
5
|
from typing import Optional
|
6
6
|
from google_news_trends_mcp import news
|
7
7
|
from typing import Annotated
|
8
|
+
from newspaper import settings as newspaper_settings
|
8
9
|
from fastmcp.server.middleware.timing import TimingMiddleware
|
9
10
|
from fastmcp.server.middleware.logging import LoggingMiddleware
|
10
11
|
from fastmcp.server.middleware.rate_limiting import RateLimitingMiddleware
|
@@ -132,11 +133,49 @@ mcp.add_middleware(TimingMiddleware()) # Time actual execution
|
|
132
133
|
mcp.add_middleware(LoggingMiddleware()) # Log everything
|
133
134
|
|
134
135
|
|
136
|
+
# Configure newspaper settings for article extraction
|
137
|
+
def set_newspaper_article_fields(full_data: bool = False):
|
138
|
+
if full_data:
|
139
|
+
newspaper_settings.article_json_fields = [
|
140
|
+
"url",
|
141
|
+
"read_more_link",
|
142
|
+
"language",
|
143
|
+
"title",
|
144
|
+
"top_image",
|
145
|
+
"meta_img",
|
146
|
+
"images",
|
147
|
+
"movies",
|
148
|
+
"keywords",
|
149
|
+
"keyword_scores",
|
150
|
+
"meta_keywords",
|
151
|
+
"tags",
|
152
|
+
"authors",
|
153
|
+
"publish_date",
|
154
|
+
"summary",
|
155
|
+
"meta_description",
|
156
|
+
"meta_lang",
|
157
|
+
"meta_favicon",
|
158
|
+
"meta_site_name",
|
159
|
+
"canonical_link",
|
160
|
+
"text",
|
161
|
+
]
|
162
|
+
else:
|
163
|
+
newspaper_settings.article_json_fields = [
|
164
|
+
"url",
|
165
|
+
"title",
|
166
|
+
"text",
|
167
|
+
"publish_date",
|
168
|
+
"summary",
|
169
|
+
"keywords",
|
170
|
+
]
|
171
|
+
|
172
|
+
|
135
173
|
@mcp.tool(
|
136
174
|
description=news.get_news_by_keyword.__doc__,
|
137
175
|
tags={"news", "articles", "keyword"},
|
138
176
|
)
|
139
177
|
async def get_news_by_keyword(
|
178
|
+
ctx: Context,
|
140
179
|
keyword: Annotated[str, Field(description="Search term to find articles.")],
|
141
180
|
period: Annotated[
|
142
181
|
int, Field(description="Number of days to look back for articles.", ge=1)
|
@@ -146,14 +185,20 @@ async def get_news_by_keyword(
|
|
146
185
|
] = 10,
|
147
186
|
nlp: Annotated[
|
148
187
|
bool, Field(description="Whether to perform NLP on the articles.")
|
149
|
-
] =
|
188
|
+
] = False,
|
189
|
+
full_data: Annotated[
|
190
|
+
bool, Field(description="Return full data for each article.")
|
191
|
+
] = False,
|
150
192
|
) -> list[ArticleOut]:
|
193
|
+
set_newspaper_article_fields(full_data)
|
151
194
|
articles = await news.get_news_by_keyword(
|
152
195
|
keyword=keyword,
|
153
196
|
period=period,
|
154
197
|
max_results=max_results,
|
155
198
|
nlp=nlp,
|
199
|
+
report_progress=ctx.report_progress,
|
156
200
|
)
|
201
|
+
await ctx.report_progress(progress=len(articles), total=len(articles))
|
157
202
|
return [ArticleOut(**a.to_json(False)) for a in articles]
|
158
203
|
|
159
204
|
|
@@ -162,6 +207,7 @@ async def get_news_by_keyword(
|
|
162
207
|
tags={"news", "articles", "location"},
|
163
208
|
)
|
164
209
|
async def get_news_by_location(
|
210
|
+
ctx: Context,
|
165
211
|
location: Annotated[str, Field(description="Name of city/state/country.")],
|
166
212
|
period: Annotated[
|
167
213
|
int, Field(description="Number of days to look back for articles.", ge=1)
|
@@ -171,14 +217,20 @@ async def get_news_by_location(
|
|
171
217
|
] = 10,
|
172
218
|
nlp: Annotated[
|
173
219
|
bool, Field(description="Whether to perform NLP on the articles.")
|
174
|
-
] =
|
220
|
+
] = False,
|
221
|
+
full_data: Annotated[
|
222
|
+
bool, Field(description="Return full data for each article.")
|
223
|
+
] = False,
|
175
224
|
) -> list[ArticleOut]:
|
225
|
+
set_newspaper_article_fields(full_data)
|
176
226
|
articles = await news.get_news_by_location(
|
177
227
|
location=location,
|
178
228
|
period=period,
|
179
229
|
max_results=max_results,
|
180
230
|
nlp=nlp,
|
231
|
+
report_progress=ctx.report_progress,
|
181
232
|
)
|
233
|
+
await ctx.report_progress(progress=len(articles), total=len(articles))
|
182
234
|
return [ArticleOut(**a.to_json(False)) for a in articles]
|
183
235
|
|
184
236
|
|
@@ -186,6 +238,7 @@ async def get_news_by_location(
|
|
186
238
|
description=news.get_news_by_topic.__doc__, tags={"news", "articles", "topic"}
|
187
239
|
)
|
188
240
|
async def get_news_by_topic(
|
241
|
+
ctx: Context,
|
189
242
|
topic: Annotated[str, Field(description="Topic to search for articles.")],
|
190
243
|
period: Annotated[
|
191
244
|
int, Field(description="Number of days to look back for articles.", ge=1)
|
@@ -195,19 +248,26 @@ async def get_news_by_topic(
|
|
195
248
|
] = 10,
|
196
249
|
nlp: Annotated[
|
197
250
|
bool, Field(description="Whether to perform NLP on the articles.")
|
198
|
-
] =
|
251
|
+
] = False,
|
252
|
+
full_data: Annotated[
|
253
|
+
bool, Field(description="Return full data for each article.")
|
254
|
+
] = False,
|
199
255
|
) -> list[ArticleOut]:
|
256
|
+
set_newspaper_article_fields(full_data)
|
200
257
|
articles = await news.get_news_by_topic(
|
201
258
|
topic=topic,
|
202
259
|
period=period,
|
203
260
|
max_results=max_results,
|
204
261
|
nlp=nlp,
|
262
|
+
report_progress=ctx.report_progress,
|
205
263
|
)
|
264
|
+
await ctx.report_progress(progress=len(articles), total=len(articles))
|
206
265
|
return [ArticleOut(**a.to_json(False)) for a in articles]
|
207
266
|
|
208
267
|
|
209
268
|
@mcp.tool(description=news.get_top_news.__doc__, tags={"news", "articles", "top"})
|
210
269
|
async def get_top_news(
|
270
|
+
ctx: Context,
|
211
271
|
period: Annotated[
|
212
272
|
int, Field(description="Number of days to look back for top articles.", ge=1)
|
213
273
|
] = 3,
|
@@ -216,13 +276,19 @@ async def get_top_news(
|
|
216
276
|
] = 10,
|
217
277
|
nlp: Annotated[
|
218
278
|
bool, Field(description="Whether to perform NLP on the articles.")
|
219
|
-
] =
|
279
|
+
] = False,
|
280
|
+
full_data: Annotated[
|
281
|
+
bool, Field(description="Return full data for each article.")
|
282
|
+
] = False,
|
220
283
|
) -> list[ArticleOut]:
|
284
|
+
set_newspaper_article_fields(full_data)
|
221
285
|
articles = await news.get_top_news(
|
222
286
|
period=period,
|
223
287
|
max_results=max_results,
|
224
288
|
nlp=nlp,
|
289
|
+
report_progress=ctx.report_progress,
|
225
290
|
)
|
291
|
+
await ctx.report_progress(progress=len(articles), total=len(articles))
|
226
292
|
return [ArticleOut(**a.to_json(False)) for a in articles]
|
227
293
|
|
228
294
|
|
@@ -243,8 +309,18 @@ async def get_trending_terms(
|
|
243
309
|
int, Field(description="Maximum number of results to return.", ge=1)
|
244
310
|
] = 100,
|
245
311
|
) -> list[TrendingTermOut]:
|
312
|
+
|
313
|
+
if not full_data:
|
314
|
+
trends = await news.get_trending_terms(
|
315
|
+
geo=geo, full_data=False, max_results=max_results
|
316
|
+
)
|
317
|
+
return [
|
318
|
+
TrendingTermOut(keyword=str(tt["keyword"]), volume=tt["volume"])
|
319
|
+
for tt in trends
|
320
|
+
]
|
321
|
+
|
246
322
|
trends = await news.get_trending_terms(
|
247
|
-
geo=geo, full_data=
|
323
|
+
geo=geo, full_data=True, max_results=max_results
|
248
324
|
)
|
249
325
|
return [TrendingTermOut(**tt.__dict__) for tt in trends]
|
250
326
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: google-news-trends-mcp
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.7
|
4
4
|
Summary: An MCP server to access Google News and Google Trends.
|
5
5
|
Author-email: Jesse Manek <jesse.manek@gmail.com>
|
6
6
|
License-Expression: MIT
|
@@ -30,7 +30,6 @@ Dynamic: license-file
|
|
30
30
|
# Google News Trends MCP
|
31
31
|
|
32
32
|
An MCP server to access Google News and Google Trends. Does not rely on any paid APIs.
|
33
|
-
The returned data currently uses a lot of tokens, so it is recommended to always use limits when making requests.
|
34
33
|
|
35
34
|
## Features
|
36
35
|
|
@@ -0,0 +1,11 @@
|
|
1
|
+
google_news_trends_mcp/__init__.py,sha256=J9O5WNvC9cNDaxecveSUvzLGOXOYO-pCHbiGopfYoIc,76
|
2
|
+
google_news_trends_mcp/__main__.py,sha256=ysiAk_xpnnW3lrLlzdIQQa71tuGBRT8WocbecBsY2Fs,87
|
3
|
+
google_news_trends_mcp/cli.py,sha256=XJNnRVpDXX2MCb8dPfDcQJWYYA4CxTuxbhvpJGeVQgs,4133
|
4
|
+
google_news_trends_mcp/news.py,sha256=2xmlwe4txaqiB8MljbhbBLmpb6tM35autGJVQ144k0s,13107
|
5
|
+
google_news_trends_mcp/server.py,sha256=7hau48vQr_a2YbLgz4MqkwsTHMuSIU8jYEkjInID4gY,11553
|
6
|
+
google_news_trends_mcp-0.1.7.dist-info/licenses/LICENSE,sha256=5dsv2ZI5EZIer0a9MktVmILVrlp5vqH_0tPIe3bRLgE,1067
|
7
|
+
google_news_trends_mcp-0.1.7.dist-info/METADATA,sha256=qkJh_vuB7gIH2Pp0TorRH_9J6ZvkKmU4JOvjsZqwtoY,4464
|
8
|
+
google_news_trends_mcp-0.1.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
9
|
+
google_news_trends_mcp-0.1.7.dist-info/entry_points.txt,sha256=eVT3xd6YJQgsWAUBwhnffuwhXNF7yyt_uco6fjBy-1o,130
|
10
|
+
google_news_trends_mcp-0.1.7.dist-info/top_level.txt,sha256=RFheDbzhNnEV_Y3iFNm7jhRhY1P1wQgfiYqVpXCTD_U,23
|
11
|
+
google_news_trends_mcp-0.1.7.dist-info/RECORD,,
|
@@ -1,11 +0,0 @@
|
|
1
|
-
google_news_trends_mcp/__init__.py,sha256=J9O5WNvC9cNDaxecveSUvzLGOXOYO-pCHbiGopfYoIc,76
|
2
|
-
google_news_trends_mcp/__main__.py,sha256=ysiAk_xpnnW3lrLlzdIQQa71tuGBRT8WocbecBsY2Fs,87
|
3
|
-
google_news_trends_mcp/cli.py,sha256=XJNnRVpDXX2MCb8dPfDcQJWYYA4CxTuxbhvpJGeVQgs,4133
|
4
|
-
google_news_trends_mcp/news.py,sha256=FYz1guxLZThMmh_9uN3VcdHBjLHZF5brhk7Bw7QxeDo,11780
|
5
|
-
google_news_trends_mcp/server.py,sha256=4l1cslLvQ2gdldRK6DTPmlaO37iLublBT9Y5_9nBj2o,9114
|
6
|
-
google_news_trends_mcp-0.1.5.dist-info/licenses/LICENSE,sha256=5dsv2ZI5EZIer0a9MktVmILVrlp5vqH_0tPIe3bRLgE,1067
|
7
|
-
google_news_trends_mcp-0.1.5.dist-info/METADATA,sha256=yKcgFJVdy-3o_Ox-F8r0xEbCTZ9ToQJXTlD53JkYtyo,4580
|
8
|
-
google_news_trends_mcp-0.1.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
9
|
-
google_news_trends_mcp-0.1.5.dist-info/entry_points.txt,sha256=eVT3xd6YJQgsWAUBwhnffuwhXNF7yyt_uco6fjBy-1o,130
|
10
|
-
google_news_trends_mcp-0.1.5.dist-info/top_level.txt,sha256=RFheDbzhNnEV_Y3iFNm7jhRhY1P1wQgfiYqVpXCTD_U,23
|
11
|
-
google_news_trends_mcp-0.1.5.dist-info/RECORD,,
|
File without changes
|
{google_news_trends_mcp-0.1.5.dist-info → google_news_trends_mcp-0.1.7.dist-info}/entry_points.txt
RENAMED
File without changes
|
{google_news_trends_mcp-0.1.5.dist-info → google_news_trends_mcp-0.1.7.dist-info}/licenses/LICENSE
RENAMED
File without changes
|
{google_news_trends_mcp-0.1.5.dist-info → google_news_trends_mcp-0.1.7.dist-info}/top_level.txt
RENAMED
File without changes
|